table-action-modal.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <template>
  2. <n-modal v-model:show="modalVisible" preset="card" :title="title" style="width: 70%">
  3. <n-form ref="formRef" label-placement="left" :label-width="80" :model="formModel" :rules="rules">
  4. <n-grid :cols="48" :x-gap="18">
  5. <n-form-item-grid-item :span="25" label="事件名称" path="name">
  6. <n-input v-model:value="formModel.name" />
  7. </n-form-item-grid-item>
  8. <n-form-item-grid-item :span="25" label="是否启用" path="is_show">
  9. <n-radio-group v-model:value="formModel.is_show">
  10. <n-radio v-for="item in EventIsShowOptions" :key="item.value" :value="item.value">{{ item.label }}</n-radio>
  11. </n-radio-group>
  12. </n-form-item-grid-item>
  13. <n-form-item-grid-item :span="25" label="事件描述" path="remarks">
  14. <n-input v-model:value="formModel.remarks" type="textarea" placeholder="请输入字段描述" />
  15. </n-form-item-grid-item>
  16. </n-grid>
  17. <n-space style="display: flex; flex-direction: column">
  18. <n-button type="primary" @click="addEventField">添加事件字段</n-button>
  19. <n-data-table :columns="columns" :data="tableData" :pagination="pagination" />
  20. </n-space>
  21. <n-space class="w-full pt-16px" :size="25" justify="end">
  22. <n-button class="w-72px" @click="closeModal">取消</n-button>
  23. <n-button class="w-72px" type="primary" @click="handleSubmit">确定</n-button>
  24. </n-space>
  25. </n-form>
  26. </n-modal>
  27. </template>
  28. <script setup lang="ts">
  29. import { ref, computed, reactive, watch, h } from 'vue';
  30. import type { FormInst, FormItemRule, DataTableColumns, PaginationProps } from 'naive-ui';
  31. import { NButton, NTag, NInput, NSelect } from 'naive-ui';
  32. import { EditSelectBoxDataSource, EditSelectBoxIsShow, EditSelectBoxShowLine, EventIsShowOptions } from '@/constants';
  33. import { createRequiredFormRule } from '@/utils';
  34. import { basicEventColumnName, fetchEventAdd, fetchEventEdit } from '@/service/api/event';
  35. export interface Props {
  36. /** 弹窗可见性 */
  37. visible: boolean;
  38. /**
  39. * 弹窗类型
  40. * add: 新增
  41. * edit: 编辑
  42. */
  43. type?: 'add' | 'edit';
  44. /** 编辑的表格行数据 */
  45. editData?: BackgroundEvent.Event | null;
  46. }
  47. export type ModalType = NonNullable<Props['type']>;
  48. defineOptions({ name: 'TableActionModal' });
  49. const props = withDefaults(defineProps<Props>(), {
  50. type: 'add',
  51. editData: null
  52. });
  53. interface Emits {
  54. (e: 'update:visible', visible: boolean): void;
  55. }
  56. /* */
  57. const emit = defineEmits<Emits>();
  58. const modalVisible = computed({
  59. get() {
  60. return props.visible;
  61. },
  62. set(visible) {
  63. emit('update:visible', visible);
  64. }
  65. });
  66. const titles: Record<ModalType, string> = {
  67. add: '添加事件',
  68. edit: '编辑事件'
  69. };
  70. const title = computed(() => {
  71. return titles[props.type];
  72. });
  73. const formRef = ref<HTMLElement & FormInst>();
  74. type FormModel = Pick<BackgroundEvent.Event, 'name' | 'is_show' | 'remarks' | 'event_field' | 'is_delete' | 'id'>;
  75. const formModel = reactive<FormModel>(createDefaultFormModel());
  76. const rules: Record<keyof FormModel, FormItemRule | FormItemRule[]> = {
  77. name: createRequiredFormRule('请输入事件名称'),
  78. is_show: createRequiredFormRule('请选择是否启用'),
  79. is_delete: createRequiredFormRule('请选择是否删除'),
  80. remarks: createRequiredFormRule('请输入事件描述'),
  81. event_field: createRequiredFormRule('请输入事件关联字段'),
  82. id: createRequiredFormRule('请输入事件id')
  83. };
  84. const selectBoxData: EventSelectBox.Data = {
  85. cowFieldData: [],
  86. isDetails: EditSelectBoxIsShow,
  87. isList: EditSelectBoxIsShow,
  88. dataSource: EditSelectBoxDataSource,
  89. isRequired: EditSelectBoxIsShow,
  90. showLine: EditSelectBoxShowLine
  91. };
  92. function setCowFieldData(data: ApiBackground.BasicCowField[] | null) {
  93. selectBoxData.cowFieldData = data;
  94. }
  95. function getCowFieldData() {
  96. const data = basicEventColumnName('animals_cow');
  97. data.then(res => {
  98. setCowFieldData(res.data);
  99. });
  100. }
  101. function createDefaultFormModel(): FormModel {
  102. return {
  103. id: 0,
  104. name: '',
  105. is_show: 1,
  106. is_delete: 2,
  107. remarks: '',
  108. event_field: null
  109. };
  110. }
  111. function handleUpdateFormModel(model: Partial<FormModel>) {
  112. Object.assign(formModel, model);
  113. }
  114. function handleUpdateFormModelByModalType() {
  115. const handlers: Record<ModalType, () => void> = {
  116. add: () => {
  117. const defaultFormModel = createDefaultFormModel();
  118. handleUpdateFormModel(defaultFormModel);
  119. },
  120. edit: () => {
  121. if (props.editData) {
  122. handleUpdateFormModel(props.editData);
  123. if (props.editData.event_field) {
  124. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  125. tableData.value = props.editData.event_field;
  126. }
  127. }
  128. }
  129. };
  130. handlers[props.type]();
  131. }
  132. const pagination: PaginationProps = reactive({
  133. page: 1,
  134. pageSize: 10,
  135. showSizePicker: true,
  136. pageSizes: [10, 15, 20, 25, 30],
  137. onChange: (page: number) => {
  138. pagination.page = page;
  139. },
  140. onUpdatePageSize: (pageSize: number) => {
  141. pagination.pageSize = pageSize;
  142. pagination.page = 1;
  143. }
  144. });
  145. const createData = (): RowField.Data[] => [
  146. {
  147. is_required: 1,
  148. field_id: 0,
  149. field_name: '',
  150. column_name: '',
  151. data_source: 1,
  152. is_list: 1,
  153. is_details: 1,
  154. show_line: 1
  155. }
  156. ];
  157. const tableData = ref(createData());
  158. const createColumns = (): DataTableColumns<RowField.Data> => [
  159. {
  160. title: '序号',
  161. key: 'orderNum',
  162. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  163. render(row, index) {
  164. return h(
  165. NTag,
  166. {
  167. style: {
  168. marginRight: '6px',
  169. background: 'none',
  170. color: 'rgb(31, 34, 37)'
  171. },
  172. type: 'info',
  173. bordered: false
  174. },
  175. {
  176. default: () => index + 1
  177. }
  178. );
  179. }
  180. },
  181. {
  182. title: '字段名称',
  183. key: 'columnName',
  184. maxWidth: 300,
  185. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  186. render(row, index) {
  187. return h(NSelect, {
  188. options: selectBoxData.cowFieldData,
  189. value: tableData.value[index].column_name,
  190. onUpdateValue(value: string) {
  191. tableData.value[index].column_name = value;
  192. }
  193. });
  194. }
  195. },
  196. {
  197. title: '是否必选/填',
  198. key: 'isRequired',
  199. maxWidth: 100,
  200. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  201. render(row, index) {
  202. return h(NSelect, {
  203. options: selectBoxData.isRequired,
  204. value: tableData.value[index].is_required,
  205. onUpdateValue(value: number) {
  206. tableData.value[index].is_required = value;
  207. }
  208. });
  209. }
  210. },
  211. {
  212. title: '数据来源',
  213. key: 'dataSource',
  214. maxWidth: 200,
  215. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  216. render(row, index) {
  217. return h(NSelect, {
  218. options: selectBoxData.dataSource,
  219. value: tableData.value[index].data_source,
  220. onUpdateValue(value: number) {
  221. tableData.value[index].data_source = value;
  222. }
  223. });
  224. }
  225. },
  226. {
  227. title: '列表是否可见',
  228. key: 'isList',
  229. maxWidth: 200,
  230. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  231. render(row, index) {
  232. return h(NSelect, {
  233. options: selectBoxData.isList,
  234. value: tableData.value[index].is_list,
  235. onUpdateValue(value: number) {
  236. tableData.value[index].is_list = value;
  237. }
  238. });
  239. }
  240. },
  241. {
  242. title: '详情页是否可见',
  243. key: 'isDetails',
  244. maxWidth: 200,
  245. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  246. render(row, index) {
  247. return h(NSelect, {
  248. options: selectBoxData.isDetails,
  249. value: tableData.value[index].is_details,
  250. onUpdateValue(value: number) {
  251. tableData.value[index].is_details = value;
  252. }
  253. });
  254. }
  255. },
  256. {
  257. title: '显示分布',
  258. key: 'showLine',
  259. maxWidth: 200,
  260. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  261. render(row, index) {
  262. return h(NSelect, {
  263. options: selectBoxData.showLine,
  264. value: tableData.value[index].show_line,
  265. onUpdateValue(value: number) {
  266. tableData.value[index].show_line = value;
  267. }
  268. });
  269. }
  270. },
  271. {
  272. title: '操作',
  273. key: 'actions',
  274. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  275. render(row, index) {
  276. return [
  277. h(
  278. NButton,
  279. {
  280. type: 'error',
  281. size: 'small',
  282. onClick: () => deleteData(index) // 点击按钮后的回调
  283. },
  284. { default: () => '删除' } // 按钮显示名称
  285. )
  286. ];
  287. }
  288. }
  289. ];
  290. const columns = ref(createColumns());
  291. // 添加事件字段
  292. function addEventField() {
  293. tableData.value.push(createData()[0]);
  294. }
  295. // 删除
  296. function deleteData(indexKey: number) {
  297. tableData.value.splice(indexKey, 1);
  298. }
  299. const closeModal = () => {
  300. tableData.value = [];
  301. modalVisible.value = false;
  302. };
  303. getCowFieldData();
  304. async function handleSubmit() {
  305. await formRef.value?.validate();
  306. formModel.event_field = tableData.value;
  307. if (props.type === 'add') {
  308. const data = fetchEventAdd(formModel);
  309. data.then(res => {
  310. if (res.data) {
  311. window.$message?.success(`${titles[props.type]}成功!`);
  312. }
  313. });
  314. }
  315. if (props.type === 'edit') {
  316. const data = fetchEventEdit(formModel);
  317. data.then(res => {
  318. if (res.data) {
  319. window.$message?.success(`${titles[props.type]}成功!`);
  320. }
  321. });
  322. }
  323. closeModal();
  324. }
  325. watch(
  326. () => props.visible,
  327. newValue => {
  328. if (newValue) {
  329. handleUpdateFormModelByModalType();
  330. }
  331. }
  332. );
  333. </script>
  334. <style scoped></style>