Эх сурвалжийг харах

feat(other): 指标列表

Yi 2 жил өмнө
parent
commit
6b44191513

+ 3 - 3
.env

@@ -1,10 +1,10 @@
 VITE_BASE_URL=/
 
-VITE_APP_NAME=SoybeanAdmin
+VITE_APP_NAME=CopartnerAdmin
 
-VITE_APP_TITLE=Soybean管理系统
+VITE_APP_TITLE=Copartner事件驱动系统
 
-VITE_APP_DESC=SoybeanAdmin是一个中后台管理系统模版
+VITE_APP_DESC=CopartnerAdmin是一个事件驱动中后台管理系统
 
 # 权限路由模式: static | dynamic
 VITE_AUTH_ROUTE_MODE=static

+ 10 - 4
src/constants/business.ts

@@ -116,8 +116,14 @@ export const EventIsShowOptions: Common.OptionWithKey<BackgroundEvent.IsShowKey>
   { value: 2, label: EventIsShowLabels['2'] }
 ];
 
-export const EventFieldOptions: Common.OptionWithKey<BackgroundEvent.IsShowKey>[] = [
-  { value: 0, label: EventIsShowLabels['0'] },
-  { value: 1, label: EventIsShowLabels['1'] },
-  { value: 2, label: EventIsShowLabels['2'] }
+export const IndicatorsIsShowLabels: Record<BackgroundIndicators.IsShowKey, string> = {
+  0: '无效',
+  1: '是',
+  2: '否'
+};
+
+export const IndicatorsOptions: Common.OptionWithKey<BackgroundEvent.IsShowKey>[] = [
+  { value: 0, label: IndicatorsIsShowLabels['0'] },
+  { value: 1, label: IndicatorsIsShowLabels['1'] },
+  { value: 2, label: IndicatorsIsShowLabels['2'] }
 ];

+ 1 - 1
src/locales/lang/zh-cn.ts

@@ -3,7 +3,7 @@ import type { LocaleMessages } from 'vue-i18n';
 const locale: LocaleMessages<I18nType.Schema> = {
   message: {
     system: {
-      title: 'Soybean管理系统'
+      title: 'Copartner事件驱动系统'
     },
     routes: {
       dashboard: {

+ 10 - 0
src/router/modules/background.ts

@@ -22,6 +22,16 @@ const dashboard: AuthRoute.Route = {
         requiresAuth: true,
         icon: 'icon-park-outline:workbench'
       }
+    },
+    {
+      name: 'background_indicators',
+      path: '/background/indicators',
+      component: 'self',
+      meta: {
+        title: '指标设置',
+        requiresAuth: true,
+        icon: 'icon-park-outline:workbench'
+      }
     }
   ],
   meta: {

+ 16 - 0
src/service/api/event.adapter.ts

@@ -25,3 +25,19 @@ export function adapterOfFetchEventList(data: ApiBackground.Event[] | null): Bac
     return event;
   });
 }
+
+export function adapterOfFetchIndicatorsList(
+  data: ApiBackground.Indicators[] | null
+): BackgroundIndicators.Indicators[] {
+  if (!data) return [];
+
+  return data.map((item, index) => {
+    const indicators: BackgroundIndicators.Indicators = {
+      index: index + 1,
+      key: item.id,
+      ...item
+    };
+
+    return indicators;
+  });
+}

+ 26 - 1
src/service/api/event.ts

@@ -1,5 +1,9 @@
 import { adapter } from '@/utils';
-import { adapterOfFetchEventList, adapterOfFetchFieldList } from '@/service/api/event.adapter';
+import {
+  adapterOfFetchEventList,
+  adapterOfFetchFieldList,
+  adapterOfFetchIndicatorsList
+} from '@/service/api/event.adapter';
 import { backgroundRequest } from '../request';
 
 /** 获取用户列表 */
@@ -35,3 +39,24 @@ export const fetchEventList = async (page: number, page_size: number, name: stri
 export const fetchEventDelete = (eventId: number) => {
   return backgroundRequest.post<ApiBoolean.OK | null>('/kpt/event/delete', { id: eventId });
 };
+
+export const fetchIndicatorsList = async (page: number, page_size: number, name: string) => {
+  const data = await backgroundRequest.post<ApiBackground.Indicators[] | null>('/kpt/indicators/search', {
+    page,
+    page_size,
+    name
+  });
+  return adapter(adapterOfFetchIndicatorsList, data);
+};
+
+export const fetchIndicatorsAdd = (params: ApiBackground.Indicators[]) => {
+  return backgroundRequest.post<ApiBoolean.OK | null>('/kpt/indicators/add', params);
+};
+
+export const fetchIndicatorsEdit = (param: ApiBackground.Indicators) => {
+  return backgroundRequest.post<ApiBoolean.OK | null>('/kpt/indicators/edit', param);
+};
+
+export const fetchIndicatorsDelete = (fieldId: number) => {
+  return backgroundRequest.post<ApiBoolean.OK | null>('/kpt/indicators/delete', { id: fieldId });
+};

+ 17 - 0
src/typings/business.d.ts

@@ -94,3 +94,20 @@ declare namespace BackgroundEvent {
    */
   type IsShowKey = NonNullable<Event['is_show']>;
 }
+
+declare namespace BackgroundIndicators {
+  interface Indicators extends ApiBackground.Indicators {
+    /** 序号 */
+    index: number;
+    /** 表格的key(id) */
+    key: number;
+  }
+
+  /**
+   * 是否启动
+   * 0 无效
+   * 1:是
+   * 2: 否
+   */
+  type IsShowKey = NonNullable<Indicators['is_show']>;
+}

+ 36 - 0
src/typings/indicators.d.ts

@@ -0,0 +1,36 @@
+declare namespace ApiBackground {
+  interface Indicators {
+    /** id */
+    id: number;
+    /** 字段名 */
+    name: string | null;
+    category_id: number | null;
+    /**
+     * 颗粒度大小
+     * 0 无效
+     * 1 年月日
+     * 2 年月
+     * 3 年
+     * 4 周
+     * 5 季度
+     */
+    particle_size: 0 | 1 | 2 | 3 | 4 | 5 | null;
+    /*
+     * 是否显示
+     * 0 无效
+     * 1 是
+     *  2 否
+     */
+    is_show: 0 | 1 | 2 | null;
+    /**
+     * 字段描述
+     */
+    description: string | null;
+  }
+}
+
+declare namespace ApiBoolean {
+  interface OK {
+    success: boolean;
+  }
+}

+ 2 - 0
src/typings/page-route.d.ts

@@ -29,6 +29,7 @@ declare namespace PageRoute {
     | 'background'
     | 'background_event'
     | 'background_field'
+    | 'background_indicators'
     | 'component'
     | 'component_button'
     | 'component_card'
@@ -91,6 +92,7 @@ declare namespace PageRoute {
     | 'auth-demo_super'
     | 'background_event'
     | 'background_field'
+    | 'background_indicators'
     | 'component_button'
     | 'component_card'
     | 'component_table'

+ 1 - 73
src/views/background/event/components/table-action-modal.vue

@@ -11,12 +11,8 @@
           </n-radio-group>
         </n-form-item-grid-item>
         <n-form-item-grid-item :span="25" label="事件描述" path="remarks">
-          <n-input v-model:value="formModel.remarks" />
+          <n-input v-model:value="formModel.remarks" type="textarea" placeholder="请输入字段描述" />
         </n-form-item-grid-item>
-        <!--        <n-form-item-grid-item :span="25" label="字段关联" path="remarks">
-          <n-select v-model:value="value" multiple :options="options" />
-          <n-select v-model:value="value" multiple disabled :options="options" />
-        </n-form-item-grid-item>-->
       </n-grid>
       <n-space class="w-full pt-16px" :size="25" justify="end">
         <n-button class="w-72px" @click="closeModal">取消</n-button>
@@ -33,74 +29,6 @@ import { NButton } from 'naive-ui';
 import { EventIsShowOptions } from '@/constants';
 import { createRequiredFormRule } from '@/utils';
 
-// eslint-disable-next-line vue/no-export-in-script-setup
-/*
-export default defineComponent({
-  setup() {
-    return {
-      value: ref(['song3']),
-      options: [
-        {
-          label: "Everybody's Got Something to Hide Except Me and My Monkey",
-          value: 'song0',
-          disabled: true
-        },
-        {
-          label: 'Drive My Car',
-          value: 'song1'
-        },
-        {
-          label: 'Norwegian Wood',
-          value: 'song2'
-        },
-        {
-          label: "You Won't See",
-          value: 'song3',
-          disabled: true
-        },
-        {
-          label: 'Nowhere Man',
-          value: 'song4'
-        },
-        {
-          label: 'Think For Yourself',
-          value: 'song5'
-        },
-        {
-          label: 'The Word',
-          value: 'song6'
-        },
-        {
-          label: 'Michelle',
-          value: 'song7',
-          disabled: true
-        },
-        {
-          label: 'What goes on',
-          value: 'song8'
-        },
-        {
-          label: 'Girl',
-          value: 'song9'
-        },
-        {
-          label: "I'm looking through you",
-          value: 'song10'
-        },
-        {
-          label: 'In My Life',
-          value: 'song11'
-        },
-        {
-          label: 'Wait',
-          value: 'song12'
-        }
-      ]
-    };
-  }
-});
-*/
-
 export interface Props {
   /** 弹窗可见性 */
   visible: boolean;

+ 1 - 1
src/views/background/field/components/table-action-modal.vue

@@ -24,7 +24,7 @@
           <n-input-number v-model:value="formModel.max_value" clearable />
         </n-form-item-grid-item>
         <n-form-item-grid-item :span="25" label="字段描述" path="description">
-          <n-input v-model:value="formModel.description" />
+          <n-input v-model:value="formModel.description" type="textarea" placeholder="请输入字段描述" />
         </n-form-item-grid-item>
       </n-grid>
       <n-space class="w-full pt-16px" :size="24" justify="end">

+ 164 - 0
src/views/background/indicators/components/table-action-modal.vue

@@ -0,0 +1,164 @@
+<template>
+  <n-modal v-model:show="modalVisible" preset="card" :title="title" class="w-700px">
+    <n-form ref="formRef" label-placement="left" :label-width="80" :model="formModel" :rules="rules">
+      <n-grid :cols="48" :x-gap="18">
+        <n-form-item-grid-item :span="25" label="指标名称" path="name">
+          <n-input v-model:value="formModel.name" />
+        </n-form-item-grid-item>
+        <n-form-item-grid-item :span="25" label="分类ID" path="category_id">
+          <n-input-number v-model:value="formModel.category_id" />
+        </n-form-item-grid-item>
+        <n-form-item-grid-item :span="25" label="是否展示" path="is_show">
+          <n-select v-model:value="formModel.is_show" :options="IndicatorsOptions" />
+        </n-form-item-grid-item>
+        <n-form-item-grid-item :span="25" label="指标描述" path="description">
+          <n-input v-model:value="formModel.description" type="textarea" placeholder="请输入指标描述" />
+        </n-form-item-grid-item>
+      </n-grid>
+      <n-space class="w-full pt-16px" :size="24" justify="end">
+        <n-button class="w-72px" @click="closeModal">取消</n-button>
+        <n-button class="w-72px" type="primary" @click="handleSubmit">确定</n-button>
+      </n-space>
+    </n-form>
+  </n-modal>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, reactive, watch } from 'vue';
+import type { FormInst, FormItemRule } from 'naive-ui';
+import { IndicatorsOptions } from '@/constants';
+import { createRequiredFormRule } from '@/utils';
+import { fetchIndicatorsAdd, fetchIndicatorsEdit } from '@/service/api/event';
+
+export interface Props {
+  /** 弹窗可见性 */
+  visible: boolean;
+  /**
+   * 弹窗类型
+   * add: 新增
+   * edit: 编辑
+   */
+  type?: 'add' | 'edit';
+  /** 编辑的表格行数据 */
+  editData?: BackgroundField.Field | null;
+}
+
+export type ModalType = NonNullable<Props['type']>;
+
+defineOptions({ name: 'TableActionModal' });
+
+const props = withDefaults(defineProps<Props>(), {
+  type: 'add',
+  editData: null
+});
+
+interface Emits {
+  (e: 'update:visible', visible: boolean): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const modalVisible = computed({
+  get() {
+    return props.visible;
+  },
+  set(visible) {
+    emit('update:visible', visible);
+  }
+});
+const closeModal = () => {
+  modalVisible.value = false;
+};
+const titles: Record<ModalType, string> = {
+  add: '添加字段',
+  edit: '编辑字段'
+};
+
+const title = computed(() => {
+  return titles[props.type];
+});
+
+const formRef = ref<HTMLElement & FormInst>();
+
+type FormModel = Pick<
+  ApiBackground.Indicators,
+  'id' | 'name' | 'category_id' | 'particle_size' | 'is_show' | 'description'
+>;
+
+const formModel = reactive<FormModel>(createDefaultFormModel());
+
+const rules: Record<keyof FormModel, FormItemRule | FormItemRule[]> = {
+  name: createRequiredFormRule('请输入指标字段名'),
+  particle_size: createRequiredFormRule('请选择指标颗粒度'),
+  category_id: createRequiredFormRule('请选择分类id'),
+  description: createRequiredFormRule('请输入字段描述'),
+  is_show: createRequiredFormRule(),
+  id: createRequiredFormRule()
+};
+
+function createDefaultFormModel(): FormModel {
+  return {
+    id: 0,
+    name: '',
+    particle_size: null,
+    category_id: null,
+    is_show: null,
+    description: ''
+  };
+}
+
+function handleUpdateFormModel(model: Partial<FormModel>) {
+  Object.assign(formModel, model);
+}
+
+function handleUpdateFormModelByModalType() {
+  const handlers: Record<ModalType, () => void> = {
+    add: () => {
+      const defaultFormModel = createDefaultFormModel();
+      handleUpdateFormModel(defaultFormModel);
+    },
+    edit: () => {
+      if (props.editData) {
+        handleUpdateFormModel(props.editData);
+      }
+    }
+  };
+
+  handlers[props.type]();
+}
+
+async function handleSubmit() {
+  formRef.value?.validate();
+  const params: Array<ApiBackground.Indicators> = [formModel];
+
+  if (props.type === 'add') {
+    const data = fetchIndicatorsAdd(params);
+    data.then(res => {
+      if (res.data) {
+        window.$message?.success(`${titles[props.type]}成功!`);
+      }
+    });
+  }
+
+  if (props.type === 'edit') {
+    const data = fetchIndicatorsEdit(formModel);
+    data.then(res => {
+      if (res.data) {
+        window.$message?.success(`${titles[props.type]}成功!`);
+      }
+    });
+  }
+  closeModal();
+}
+
+watch(
+  () => props.visible,
+  newValue => {
+    if (newValue) {
+      handleUpdateFormModelByModalType();
+    }
+  }
+);
+</script>
+
+<style scoped></style>

+ 190 - 0
src/views/background/indicators/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div class="h-full overflow-hidden">
+    <n-card title="指标管理" :bordered="false" class="rounded-16px shadow-sm">
+      <n-space class="pb-12px" justify="space-between">
+        <n-space>
+          <n-button type="primary" @click="handleAddTable">
+            <icon-ic-round-plus class="mr-4px text-20px" />
+            新增
+          </n-button>
+          <n-button type="success">
+            <icon-uil:export class="mr-4px text-20px" />
+            导出Excel
+          </n-button>
+        </n-space>
+        <n-space align="center" :size="18">
+          <n-input-group>
+            <n-input v-model:value="indicatorsName" />
+            <n-button type="primary" @click="handleSearch">搜索</n-button>
+          </n-input-group>
+          <n-button size="small" type="primary" @click="getTableData">
+            <icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" />
+            刷新表格
+          </n-button>
+        </n-space>
+      </n-space>
+      <n-data-table :columns="columns" :data="tableData" :loading="loading" :pagination="pagination" />
+      <table-action-modal v-model:visible="visible" :type="modalType" :edit-data="editData" />
+    </n-card>
+  </div>
+</template>
+
+<script setup lang="tsx">
+import { reactive, ref } from 'vue';
+import type { Ref } from 'vue';
+import { NButton, NPopconfirm, NSpace } from 'naive-ui';
+import type { DataTableColumns, PaginationProps } from 'naive-ui';
+import { useBoolean, useLoading } from '@/hooks';
+import { fetchIndicatorsDelete, fetchIndicatorsList } from '@/service/api/event';
+import TableActionModal from './components/table-action-modal.vue';
+import type { ModalType } from './components/table-action-modal.vue';
+
+const { loading, startLoading, endLoading } = useLoading(false);
+const { bool: visible, setTrue: openModal } = useBoolean();
+const indicatorsName = ref('');
+const tableData = ref<BackgroundIndicators.Indicators[]>([]);
+function setTableData(data: BackgroundIndicators.Indicators[]) {
+  tableData.value = data;
+}
+
+async function getTableData() {
+  startLoading();
+  const { data } = await fetchIndicatorsList(1, 100, indicatorsName.value);
+  if (data) {
+    setTimeout(() => {
+      setTableData(data);
+      endLoading();
+    }, 1000);
+  }
+}
+
+const columns: Ref<DataTableColumns<BackgroundIndicators.Indicators>> = ref([
+  {
+    type: 'selection',
+    align: 'center'
+  },
+  {
+    key: 'index',
+    title: '序号',
+    align: 'center'
+  },
+  {
+    key: 'name',
+    title: '指标名称',
+    align: 'center'
+  },
+  {
+    key: 'category_id',
+    title: '分类ID',
+    align: 'center'
+  },
+  {
+    key: 'particle_size',
+    title: '指标颗粒度',
+    align: 'center'
+  },
+  {
+    key: 'description',
+    title: '字段描述',
+    align: 'center'
+  },
+  {
+    key: 'actions',
+    title: '操作',
+    align: 'center',
+    render: row => {
+      return (
+        <NSpace justify={'center'}>
+          <NButton type="info" size={'small'} onClick={() => handleEditTable(row.id)}>
+            编辑
+          </NButton>
+          <NPopconfirm onPositiveClick={() => handleDeleteTable(row.id)}>
+            {{
+              default: () => '确认删除',
+              trigger: () => (
+                <NButton type="error" size={'small'}>
+                  删除
+                </NButton>
+              )
+            }}
+          </NPopconfirm>
+        </NSpace>
+      );
+    }
+  }
+]) as Ref<DataTableColumns<BackgroundIndicators.Indicators>>;
+
+const modalType = ref<ModalType>('add');
+
+function setModalType(type: ModalType) {
+  modalType.value = type;
+}
+
+const editData = ref<BackgroundIndicators.Indicators | null>(null);
+
+function setEditData(data: BackgroundIndicators.Indicators | null) {
+  editData.value = data;
+}
+
+function handleAddTable() {
+  openModal();
+  setModalType('add');
+}
+
+function handleEditTable(rowId: number) {
+  const findItem = tableData.value.find(item => item.id === rowId);
+  if (findItem) {
+    setEditData(findItem);
+  }
+  setModalType('edit');
+  openModal();
+}
+
+function handleDeleteTable(rowId: number) {
+  const data = fetchIndicatorsDelete(rowId);
+  data.then(res => {
+    if (res.data) {
+      window.$message?.success('删除成功!');
+    }
+  });
+  init();
+}
+
+const pagination: PaginationProps = reactive({
+  page: 1,
+  pageSize: 10,
+  showSizePicker: true,
+  pageSizes: [10, 15, 20, 25, 30],
+  onChange: (page: number) => {
+    pagination.page = page;
+  },
+  onUpdatePageSize: (pageSize: number) => {
+    pagination.pageSize = pageSize;
+    pagination.page = 1;
+  }
+});
+
+function handleSearch() {
+  if (!indicatorsName.value) {
+    window.$message?.warning('请输入字段名称');
+  } else {
+    startLoading();
+    /* let data = fetchFieldList(1, 10, fieldName.value);
+		if (data) {
+			setTimeout(() => {
+				setTableData(data);
+				endLoading();
+			}, 1000);
+		} */
+  }
+}
+
+function init() {
+  getTableData();
+}
+
+// 初始化
+init();
+</script>
+
+<style scoped></style>

+ 1 - 0
src/views/index.ts

@@ -15,6 +15,7 @@ export const views: Record<
   'auth-demo_super': () => import('./auth-demo/super/index.vue'),
   background_event: () => import('./background/event/index.vue'),
   background_field: () => import('./background/field/index.vue'),
+  background_indicators: () => import('./background/indicators/index.vue'),
   component_button: () => import('./component/button/index.vue'),
   component_card: () => import('./component/card/index.vue'),
   component_table: () => import('./component/table/index.vue'),