data.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <template>
  2. <div class="p-2">
  3. <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
  4. <div v-show="showSearch" class="mb-[10px]">
  5. <el-card shadow="hover">
  6. <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="140px">
  7. <el-form-item :label="t('dict.search.dictType')" prop="dictType">
  8. <el-select v-model="queryParams.dictType">
  9. <el-option v-for="item in typeOptions" :key="item.dictId" :label="item.dictName" :value="item.dictType" />
  10. </el-select>
  11. </el-form-item>
  12. <el-form-item :label="t('dict.search.dictLabel')" prop="dictLabel">
  13. <el-input v-model="queryParams.dictLabel" :placeholder="t('dict.search.dictLabelPlaceholder')" clearable @keyup.enter="handleQuery" />
  14. </el-form-item>
  15. <el-form-item>
  16. <el-button type="primary" icon="Search" @click="handleQuery">{{ t('dict.search.search') }}</el-button>
  17. <el-button icon="Refresh" @click="resetQuery">{{ t('dict.search.reset') }}</el-button>
  18. </el-form-item>
  19. </el-form>
  20. </el-card>
  21. </div>
  22. </transition>
  23. <el-card shadow="hover">
  24. <template #header>
  25. <el-row :gutter="10" class="mb8">
  26. <el-col :span="1.5">
  27. <el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" @click="handleAdd">{{ t('dict.button.add') }}</el-button>
  28. </el-col>
  29. <el-col :span="1.5">
  30. <el-button v-hasPermi="['system:dict:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">{{ t('dict.button.edit') }}</el-button>
  31. </el-col>
  32. <el-col :span="1.5">
  33. <el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
  34. {{ t('dict.button.delete') }}
  35. </el-button>
  36. </el-col>
  37. <el-col :span="1.5">
  38. <el-button v-hasPermi="['system:dict:export']" type="warning" plain icon="Download" @click="handleExport">{{ t('dict.button.export') }}</el-button>
  39. </el-col>
  40. <el-col :span="1.5">
  41. <el-button type="warning" plain icon="Close" @click="handleClose">{{ t('dict.button.close') }}</el-button>
  42. </el-col>
  43. <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
  44. </el-row>
  45. </template>
  46. <el-table v-loading="loading" border :data="dataList" @selection-change="handleSelectionChange">
  47. <el-table-column type="selection" width="55" align="center" />
  48. <el-table-column v-if="false" :label="t('dict.table.dictCode')" align="center" prop="dictCode" />
  49. <el-table-column :label="t('dict.table.dictLabel')" align="center" prop="dictLabel">
  50. <template #default="scope">
  51. <span
  52. v-if="(scope.row.listClass === '' || scope.row.listClass === 'default') && (scope.row.cssClass === '' || scope.row.cssClass == null)"
  53. >{{ parseI18nName(scope.row.dictLabel) }}</span
  54. >
  55. <el-tag
  56. v-else
  57. :type="scope.row.listClass === 'primary' || scope.row.listClass === 'default' ? 'primary' : scope.row.listClass"
  58. :class="scope.row.cssClass"
  59. >{{ parseI18nName(scope.row.dictLabel) }}</el-tag
  60. >
  61. </template>
  62. </el-table-column>
  63. <el-table-column :label="t('dict.table.dictValue')" align="center" prop="dictValue" />
  64. <el-table-column :label="t('dict.table.dictSort')" align="center" prop="dictSort" />
  65. <el-table-column :label="t('dict.table.remark')" align="center" prop="remark" :show-overflow-tooltip="true" />
  66. <el-table-column :label="t('dict.table.createTime')" align="center" prop="createTime" width="180">
  67. <template #default="scope">
  68. <span>{{ proxy.parseTime(scope.row.createTime) }}</span>
  69. </template>
  70. </el-table-column>
  71. <el-table-column :label="t('dict.table.operation')" align="center" width="160" class-name="small-padding fixed-width">
  72. <template #default="scope">
  73. <el-tooltip :content="t('dict.tooltip.edit')" placement="top">
  74. <el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
  75. </el-tooltip>
  76. <el-tooltip :content="t('dict.tooltip.delete')" placement="top">
  77. <el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
  78. </el-tooltip>
  79. </template>
  80. </el-table-column>
  81. </el-table>
  82. <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
  83. </el-card>
  84. <!-- 添加或修改参数配置对话框 -->
  85. <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
  86. <el-form ref="dataFormRef" :model="form" :rules="rules" label-width="80px">
  87. <el-form-item :label="t('dict.form.dictType')">
  88. <el-input v-model="form.dictType" :disabled="true" />
  89. </el-form-item>
  90. <el-form-item :label="t('dict.form.dictLabelZh')" prop="dictLabelZh">
  91. <el-input v-model="form.dictLabelZh" :placeholder="t('dict.form.dictLabelZhPlaceholder')" />
  92. </el-form-item>
  93. <el-form-item :label="t('dict.form.dictLabelEn')" prop="dictLabelEn">
  94. <el-input v-model="form.dictLabelEn" :placeholder="t('dict.form.dictLabelEnPlaceholder')" />
  95. </el-form-item>
  96. <el-form-item :label="t('dict.form.dictValue')" prop="dictValue">
  97. <el-input v-model="form.dictValue" :placeholder="t('dict.form.dictValuePlaceholder')" />
  98. </el-form-item>
  99. <el-form-item :label="t('dict.form.cssClass')" prop="cssClass">
  100. <el-input v-model="form.cssClass" :placeholder="t('dict.form.cssClassPlaceholder')" />
  101. </el-form-item>
  102. <el-form-item :label="t('dict.form.dictSort')" prop="dictSort">
  103. <el-input-number v-model="form.dictSort" controls-position="right" :min="0" />
  104. </el-form-item>
  105. <el-form-item :label="t('dict.form.listClass')" prop="listClass">
  106. <el-select v-model="form.listClass">
  107. <el-option
  108. v-for="item in listClassOptions"
  109. :key="item.value"
  110. :label="item.label + '(' + item.value + ')'"
  111. :value="item.value"
  112. ></el-option>
  113. </el-select>
  114. </el-form-item>
  115. <el-form-item :label="t('dict.form.remark')" prop="remark">
  116. <el-input v-model="form.remark" type="textarea" :placeholder="t('dict.form.remarkPlaceholder')"></el-input>
  117. </el-form-item>
  118. </el-form>
  119. <template #footer>
  120. <div class="dialog-footer">
  121. <el-button type="primary" @click="submitForm">{{ t('dict.button.submit') }}</el-button>
  122. <el-button @click="cancel">{{ t('dict.button.cancel') }}</el-button>
  123. </div>
  124. </template>
  125. </el-dialog>
  126. </div>
  127. </template>
  128. <script setup name="Data" lang="ts">
  129. import { useDictStore } from '@/store/modules/dict';
  130. import { optionselect as getDictOptionselect, getType } from '@/api/system/dict/type';
  131. import { listData, getData, delData, addData, updateData } from '@/api/system/dict/data';
  132. import { DictTypeVO } from '@/api/system/dict/type/types';
  133. import { DictDataForm, DictDataQuery, DictDataVO } from '@/api/system/dict/data/types';
  134. import { RouteLocationNormalized } from 'vue-router';
  135. import { parseI18nName } from '@/utils/i18n';
  136. import { useI18n } from 'vue-i18n';
  137. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  138. const route = useRoute();
  139. const { t } = useI18n();
  140. const dataList = ref<DictDataVO[]>([]);
  141. const loading = ref(true);
  142. const showSearch = ref(true);
  143. const ids = ref<Array<string | number>>([]);
  144. const single = ref(true);
  145. const multiple = ref(true);
  146. const total = ref(0);
  147. const defaultDictType = ref('');
  148. const typeOptions = ref<DictTypeVO[]>([]);
  149. const dataFormRef = ref<ElFormInstance>();
  150. const queryFormRef = ref<ElFormInstance>();
  151. const dialog = reactive<DialogOption>({
  152. visible: false,
  153. title: ''
  154. });
  155. // 数据标签回显样式
  156. const listClassOptions = computed(() => [
  157. { value: 'default', label: t('dict.listClassOptions.default') },
  158. { value: 'primary', label: t('dict.listClassOptions.primary') },
  159. { value: 'success', label: t('dict.listClassOptions.success') },
  160. { value: 'info', label: t('dict.listClassOptions.info') },
  161. { value: 'warning', label: t('dict.listClassOptions.warning') },
  162. { value: 'danger', label: t('dict.listClassOptions.danger') }
  163. ]);
  164. const initFormData: DictDataForm = {
  165. dictCode: undefined,
  166. dictLabel: '',
  167. dictLabelZh: '',
  168. dictLabelEn: '',
  169. dictValue: '',
  170. cssClass: '',
  171. listClass: 'primary',
  172. dictSort: 0,
  173. remark: ''
  174. };
  175. const data = reactive<PageData<DictDataForm, DictDataQuery>>({
  176. form: { ...initFormData },
  177. queryParams: {
  178. pageNum: 1,
  179. pageSize: 10,
  180. dictName: '',
  181. dictType: '',
  182. dictLabel: ''
  183. },
  184. rules: {
  185. dictLabelZh: [{ required: true, message: t('dict.rule.dictLabelZhRequired'), trigger: 'blur' }],
  186. dictLabelEn: [{ required: true, message: t('dict.rule.dictLabelEnRequired'), trigger: 'blur' }],
  187. dictValue: [{ required: true, message: t('dict.rule.dictValueRequired'), trigger: 'blur' }],
  188. dictSort: [{ required: true, message: t('dict.rule.dictSortRequired'), trigger: 'blur' }]
  189. }
  190. });
  191. const { queryParams, form, rules } = toRefs(data);
  192. /** 查询字典类型详细 */
  193. const getTypes = async (dictId: string | number) => {
  194. const { data } = await getType(dictId);
  195. queryParams.value.dictType = data.dictType;
  196. defaultDictType.value = data.dictType;
  197. getList();
  198. };
  199. /** 查询字典类型列表 */
  200. const getTypeList = async () => {
  201. const res = await getDictOptionselect();
  202. typeOptions.value = res.data;
  203. };
  204. /** 查询字典数据列表 */
  205. const getList = async () => {
  206. loading.value = true;
  207. const res = await listData(queryParams.value);
  208. dataList.value = res.rows;
  209. total.value = res.total;
  210. loading.value = false;
  211. };
  212. /** 取消按钮 */
  213. const cancel = () => {
  214. dialog.visible = false;
  215. reset();
  216. };
  217. /** 表单重置 */
  218. const reset = () => {
  219. form.value = { ...initFormData };
  220. dataFormRef.value?.resetFields();
  221. };
  222. /** 搜索按钮操作 */
  223. const handleQuery = () => {
  224. queryParams.value.pageNum = 1;
  225. getList();
  226. };
  227. /** 返回按钮操作 */
  228. const handleClose = () => {
  229. const obj: RouteLocationNormalized = {
  230. fullPath: '',
  231. hash: '',
  232. matched: [],
  233. meta: undefined,
  234. name: undefined,
  235. params: undefined,
  236. query: undefined,
  237. redirectedFrom: undefined,
  238. path: '/system/dict'
  239. };
  240. proxy?.$tab.closeOpenPage(obj);
  241. };
  242. /** 重置按钮操作 */
  243. const resetQuery = () => {
  244. queryFormRef.value?.resetFields();
  245. queryParams.value.dictType = defaultDictType.value;
  246. handleQuery();
  247. };
  248. /** 新增按钮操作 */
  249. const handleAdd = () => {
  250. reset();
  251. form.value.dictType = queryParams.value.dictType;
  252. dialog.visible = true;
  253. dialog.title = t('dict.dialog.addData');
  254. };
  255. /** 多选框选中数据 */
  256. const handleSelectionChange = (selection: DictDataVO[]) => {
  257. ids.value = selection.map((item) => item.dictCode);
  258. single.value = selection.length != 1;
  259. multiple.value = !selection.length;
  260. };
  261. /** 修改按钮操作 */
  262. const handleUpdate = async (row?: DictDataVO) => {
  263. reset();
  264. const dictCode = row?.dictCode || ids.value[0];
  265. const res = await getData(dictCode);
  266. Object.assign(form.value, res.data);
  267. // 解析国际化标签
  268. parseDictLabelForEdit(res.data.dictLabel);
  269. dialog.visible = true;
  270. dialog.title = t('dict.dialog.editData');
  271. };
  272. /** 提交按钮 */
  273. const submitForm = () => {
  274. dataFormRef.value?.validate(async (valid: boolean) => {
  275. if (valid) {
  276. // 合成国际化标签JSON字符串
  277. const dictLabelJson = JSON.stringify({
  278. zh_CN: form.value.dictLabelZh,
  279. en_US: form.value.dictLabelEn
  280. });
  281. const submitData = { ...form.value, dictLabel: dictLabelJson };
  282. form.value.dictCode ? await updateData(submitData) : await addData(submitData);
  283. useDictStore().removeDict(queryParams.value.dictType);
  284. proxy?.$modal.msgSuccess(t('dict.message.operationSuccess'));
  285. dialog.visible = false;
  286. await getList();
  287. }
  288. });
  289. };
  290. /** 删除按钮操作 */
  291. const handleDelete = async (row?: DictDataVO) => {
  292. const dictCodes = row?.dictCode || ids.value;
  293. const labelText = row ? parseI18nName(row.dictLabel) : dictCodes;
  294. await proxy?.$modal.confirm(t('dict.message.deleteDataConfirm', { label: labelText }));
  295. await delData(dictCodes);
  296. await getList();
  297. proxy?.$modal.msgSuccess(t('dict.message.deleteSuccess'));
  298. useDictStore().removeDict(queryParams.value.dictType);
  299. };
  300. /** 导出按钮操作 */
  301. const handleExport = () => {
  302. proxy?.download(
  303. 'system/dict/data/export',
  304. {
  305. ...queryParams.value
  306. },
  307. `dict_data_${new Date().getTime()}.xlsx`
  308. );
  309. };
  310. /** 解析国际化字典标签用于编辑 */
  311. const parseDictLabelForEdit = (dictLabel: string) => {
  312. try {
  313. const labelObj = JSON.parse(dictLabel);
  314. form.value.dictLabelZh = labelObj.zh_CN || '';
  315. form.value.dictLabelEn = labelObj.en_US || '';
  316. } catch (e) {
  317. // 如果解析失败,将原值设为中文标签
  318. form.value.dictLabelZh = dictLabel;
  319. form.value.dictLabelEn = '';
  320. }
  321. };
  322. onMounted(() => {
  323. getTypes(route.params && (route.params.dictId as string));
  324. getTypeList();
  325. });
  326. </script>