Files
youlai-app/src/pages/work/dict/item/index.vue

291 lines
8.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page page--padding">
<view>
<wd-search
v-model="queryParams.keywords"
placeholder="搜索字典标签/字典值"
hide-cancel
@search="handleSearch"
/>
</view>
<view class="mt-16rpx">
<wd-card
v-for="item in pageData"
:key="item.id"
custom-class="item-card"
@click="openItemDialog(item.id)"
>
<view class="flex-start">
<view class="flex-1">
<view class="flex-start">
<text class="font-bold text-32rpx">{{ item.label }}</text>
</view>
<text class="text-24rpx color-text-secondary">
字典值{{ item.value }} · 排序{{ item.sort }}
</text>
</view>
<wd-tag :type="item.status === 1 ? 'success' : 'danger'" plain>
{{ item.status === 1 ? "启用" : "禁用" }}
</wd-tag>
</view>
<view class="flex-between mt-16rpx">
<text class="text-24rpx color-text-placeholder">{{ item.remark || "暂无备注" }}</text>
<view
class="w-88rpx h-88rpx flex-center rounded-full"
hover-class="bg-[var(--color-text-placeholder)]/16"
@click.stop="showItemActions(item)"
>
<wd-icon name="more" size="18" class="color-text-secondary" />
</view>
</view>
</wd-card>
<wd-loadmore v-if="total > 0" :state="loadMoreState" @reload="fetchItemList" />
<wd-status-tip v-else-if="total === 0" image="search" tip="暂无数据" />
</view>
<wd-popup
v-model="dialog.visible"
position="bottom"
custom-class="popup-bottom"
@close="closeItemDialog"
>
<view class="p-4">
<view class="popup-title">
{{ formData.id ? "编辑字典数据" : "新增字典数据" }}
</view>
<wd-form ref="formRef" :model="formData" :rules="rules">
<wd-cell-group border>
<wd-input v-model="formData.label" label="字典项标签" required />
<wd-input v-model="formData.value" label="字典项值" required />
<wd-cell title="状态">
<wd-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
</wd-cell>
<wd-cell title="排序">
<wd-input-number v-model="formData.sort" :min="0" />
</wd-cell>
<wd-textarea
v-model="formData.remark"
label="备注"
placeholder="请输入备注"
:maxlength="100"
show-word-limit
/>
</wd-cell-group>
</wd-form>
<view class="popup-actions">
<wd-button type="info" plain @click="closeItemDialog">取消</wd-button>
<wd-button type="primary" :loading="isSubmitting" @click="submitItemForm">保存</wd-button>
</view>
</view>
</wd-popup>
<wd-action-sheet
v-model="actionSheetVisible"
:actions="actionSheetActions"
cancel-text="取消"
@select="handleActionSelect"
/>
<wd-fab
v-if="hasPermission('sys:dict-item:create') && !dialog.visible"
@click="openItemDialog()"
/>
</view>
</template>
<script lang="ts" setup>
import { onLoad, onReachBottom } from "@dcloudio/uni-app";
import { LoadMoreState } from "wot-design-uni/components/wd-loadmore/types";
import { FormRules } from "wot-design-uni/components/wd-form/types";
import { useToast, useMessage } from "wot-design-uni";
import DictAPI, { type DictItemForm, type DictItemPageQuery, type DictDataItem } from "@/api/dict";
import { hasPermission } from "@/utils/permission";
const toast = useToast();
const { messageBox } = useMessage();
const dictCode = ref<string>("");
const pageTitle = ref<string>("字典数据");
const loadMoreState = ref<LoadMoreState>("loading");
const formRef = ref();
const isSubmitting = ref(false);
const queryParams = reactive<DictItemPageQuery>({ pageNum: 1, pageSize: 10, keywords: "" });
const total = ref(0);
const pageData = ref<DictDataItem[]>([]);
const dialog = reactive({ visible: false });
const initialFormData: DictItemForm = {
id: undefined,
dictCode: undefined,
label: undefined,
value: undefined,
sort: 1,
status: 1,
remark: undefined,
};
const formData = reactive<DictItemForm>({ ...initialFormData });
const rules: FormRules = {
label: [{ required: true, message: "请输入字典项标签" }],
value: [{ required: true, message: "请输入字典项值" }],
};
const handleSearch = () => loadItemList();
function loadItemList() {
queryParams.pageNum = 1;
fetchItemList();
}
function fetchItemList() {
if (!dictCode.value) {
pageData.value = [];
total.value = 0;
loadMoreState.value = "finished";
return;
}
loadMoreState.value = "loading";
DictAPI.getItemPage(dictCode.value, queryParams)
.then((data: any) => {
pageData.value = data.list;
total.value = data.total;
queryParams.pageNum++;
})
.catch(() => {
pageData.value = [];
})
.finally(() => {
loadMoreState.value = "finished";
});
}
async function openItemDialog(id?: string) {
formRef.value?.reset();
Object.assign(formData, initialFormData);
dialog.visible = true;
formData.dictCode = dictCode.value;
if (id) {
formData.id = id;
const data = await DictAPI.getItemFormData(dictCode.value, id);
Object.assign(formData, data, { id });
}
}
function submitItemForm() {
formRef.value.validate().then(({ valid }: { valid: boolean }) => {
if (!valid) return;
if (!dictCode.value) {
toast.error("缺少字典编码");
return;
}
isSubmitting.value = true;
const id = formData.id;
const action = id
? DictAPI.updateItem(dictCode.value, id, formData)
: DictAPI.createItem(dictCode.value, formData);
action
.then(() => {
toast.success("操作成功");
closeItemDialog();
loadItemList();
})
.finally(() => {
isSubmitting.value = false;
});
});
}
function closeItemDialog() {
dialog.visible = false;
formRef.value?.reset();
Object.assign(formData, initialFormData);
}
const actionSheetVisible = ref(false);
const actionSheetActions = ref<{ name: string; color?: string }[]>([]);
const pendingAction = ref<Record<string, () => void>>({});
function showItemActions(item: DictDataItem) {
const actions: { name: string; color?: string }[] = [];
const actionMap: Record<string, () => void> = {};
if (hasPermission("sys:dict-item:update")) {
actions.push({ name: "编辑" });
actionMap["编辑"] = () => openItemDialog(item.id);
}
if (hasPermission("sys:dict-item:delete")) {
actions.push({ name: "删除", color: "var(--color-danger)" });
actionMap["删除"] = async () => {
try {
await messageBox({
title: "确认删除",
msg: `确定要删除字典数据「${item.label}」吗?`,
type: "warning",
});
if (item.id) {
await DictAPI.deleteItems(dictCode.value, String(item.id));
toast.success("删除成功");
loadItemList();
}
} catch {
// 用户取消操作
}
};
}
if (actions.length === 0) {
toast.warning("暂无操作权限");
return;
}
actionSheetActions.value = actions;
pendingAction.value = actionMap;
actionSheetVisible.value = true;
}
function handleActionSelect({ value }: { value: string }) {
pendingAction.value[value]?.();
}
onReachBottom(() => {
if (queryParams.pageNum * queryParams.pageSize < total.value) {
fetchItemList();
} else {
loadMoreState.value = "finished";
}
});
onLoad((query) => {
dictCode.value = String((query as any)?.dictCode || "");
pageTitle.value = String((query as any)?.title || "字典数据");
if (pageTitle.value) {
uni.setNavigationBarTitle({ title: pageTitle.value });
}
loadItemList();
});
</script>
<script lang="ts">
export default { options: { styleIsolation: "shared" } };
</script>
<route lang="json">
{
"name": "dict-item",
"style": {
"navigationBarTitleText": "字典数据"
}
}
</route>