Commit ac57eaaa1c89dc85a923fa40a9645bc22ec55581

Authored by 严涛
1 parent 1cfc0eb0

设备类型

  1 +import { defHttp } from '/@/utils/http/axios';
  2 +import { omit } from 'lodash-es';
  3 +
  4 +/**
  5 + * 获取列表
  6 + */
  7 +export const getEquipmentTypeList = (params) => {
  8 + const { page, pageSize } = params;
  9 + const otherParams = omit(params, ['page', 'pageSize']);
  10 + return defHttp.post<any>({
  11 + url: `/brain/category/pageData?page=${page}&pageSize=${pageSize}`,
  12 + params: otherParams,
  13 + });
  14 +};
  15 +
  16 +/**
  17 + * 新增
  18 + */
  19 +export const saveEquipmentType = (params) => {
  20 + return defHttp.post<any>({
  21 + url: `/brain/category/save`,
  22 + params,
  23 + });
  24 +};
  25 +
  26 +/**
  27 + *详情
  28 + */
  29 +export const getEquipmentTypeDetail = (params) => {
  30 + return defHttp.get<any>({
  31 + url: `/brain/category/detail`,
  32 + params,
  33 + });
  34 +};
  35 +
  36 +/**
  37 + *删除
  38 + */
  39 +export const deleteEquipmentType = (params) => {
  40 + return defHttp.delete<any>({
  41 + url: `/brain/category`,
  42 + params,
  43 + });
  44 +};
  45 +
  46 +/**
  47 + *停用 启用
  48 + */
  49 +export const updateEquipmentTypeStatus = (params) => {
  50 + return defHttp.post<any>({
  51 + url: `/brain/category/updateStatus`,
  52 + params,
  53 + });
  54 +};
... ...
  1 +export default {
  2 + typeListText: '设备类型列表',
  3 + deviceName: '设备名称',
  4 + deviceCode: '设备编码',
  5 + equipmentTypeName: '设备类型名称',
  6 + equipmentTypeCode: '设备类型编码',
  7 + equipmentTypeCodeOrName: '设备类型编码或名称',
  8 + status: '状态',
  9 + remark: '备注',
  10 + creator: '创建人',
  11 + createTime: '创建时间',
  12 +};
... ...
  1 +<template>
  2 + <BasicDrawer
  3 + destroyOnClose
  4 + v-bind="$attrs"
  5 + showFooter
  6 + width="35%"
  7 + :maskClosable="true"
  8 + :title="titleText"
  9 + @register="registerDrawer"
  10 + wrapClassName="report-drawer"
  11 + @ok="handleSubmit"
  12 + @close="handleClose"
  13 + >
  14 + <BasicForm @register="registerForm" />
  15 + </BasicDrawer>
  16 +</template>
  17 +<script setup lang="ts">
  18 + import { nextTick, reactive, ref } from 'vue';
  19 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  20 + import { SchemaFiled } from '../../config/enum';
  21 + import { BasicForm, useForm } from '/@/components/Form';
  22 + import { formSchema } from '../../config/data';
  23 + import { useHooks } from '/@/views/report/config/hooks/index.hooks';
  24 + import { getEquipmentTypeDetail, saveEquipmentType } from '/@/api/archive/type';
  25 + import { useThrottleFn } from '@vueuse/shared/index';
  26 + import { useMessage } from '/@/hooks/web/useMessage';
  27 + import { useI18n } from '/@/hooks/web/useI18n';
  28 + const { setDefaultTime, disableCustomWeekly, removeFields } = useHooks();
  29 + const restData = reactive({
  30 + data: {},
  31 + });
  32 + const { createMessage } = useMessage();
  33 + const { t } = useI18n();
  34 +
  35 + const emits = defineEmits(['success', 'register']);
  36 + const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
  37 + labelWidth: 140,
  38 + schemas: formSchema,
  39 + showActionButtonGroup: false,
  40 + fieldMapToTime: [[SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS]]],
  41 + });
  42 +
  43 + const titleText = ref('');
  44 + const businessText = ref('');
  45 + const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
  46 + try {
  47 + await nextTick();
  48 + handleClose();
  49 + businessText.value = data.text;
  50 + titleText.value =
  51 + data.text === 'add'
  52 + ? '创建设备类型'
  53 + : data.text === 'edit'
  54 + ? '编辑设备类型'
  55 + : '查看设备类型';
  56 + setFieldsValue(setDefaultTime());
  57 + updateSchema(disableCustomWeekly(0));
  58 + if (businessText.value === 'add') return;
  59 + const rest = await getEquipmentTypeDetail({ id: data.record?.id });
  60 + restData.data = rest ?? {};
  61 + await setFieldsValue({
  62 + ...restData.data,
  63 + });
  64 + } finally {
  65 + setDrawerProps({ loading: false });
  66 + }
  67 + });
  68 + const handleClose = () => {
  69 + resetValue();
  70 + };
  71 + //重置表单
  72 + const resetValue = () => {
  73 + resetFields();
  74 + };
  75 +
  76 + const handleSubmit = () => {
  77 + if (businessText.value === 'view') {
  78 + closeDrawer();
  79 + handleClose();
  80 + return;
  81 + }
  82 + useThrottle();
  83 + };
  84 +
  85 + const useThrottle = useThrottleFn(() => {
  86 + getValue();
  87 + }, 2000);
  88 +
  89 + const getValue = async () => {
  90 + try {
  91 + setDrawerProps({ confirmLoading: true });
  92 + const values = await validate();
  93 + if (!values) return;
  94 + const data = {
  95 + ...values,
  96 + };
  97 + removeFields.forEach((item) => {
  98 + Reflect.deleteProperty(data, item);
  99 + });
  100 + businessText.value === 'add'
  101 + ? await saveEquipmentType(data)
  102 + : saveEquipmentType({ ...restData.data, ...data });
  103 + createMessage.success(
  104 + t(businessText.value !== 'add' ? 'common.editSuccessText' : 'common.createSuccessText')
  105 + );
  106 + closeDrawer();
  107 + handleClose();
  108 + setTimeout(() => {
  109 + emits('success');
  110 + }, 500);
  111 + } finally {
  112 + setDrawerProps({ confirmLoading: false });
  113 + }
  114 + };
  115 +</script>
  116 +<style scoped>
  117 + .inputTitle::before {
  118 + display: inline-block;
  119 + margin-right: 4px;
  120 + color: #ff4d4f;
  121 + font-size: 14px;
  122 + font-family: SimSun, sans-serif;
  123 + line-height: 1;
  124 + content: '*';
  125 + }
  126 +</style>
... ...
  1 +import TypeDrawer from './TypeDrawer.vue';
  2 +
  3 +export { TypeDrawer };
... ...
  1 +import { FormSchema as BFormSchema } from '../../../../components/Form';
  2 +import { useI18n } from '/@/hooks/web/useI18n';
  3 +const { t } = useI18n();
  4 +const statusOptions = [
  5 + { label: t('common.enableText'), value: 'ENABLE' },
  6 + { label: t('common.stop'), value: 'STOP' },
  7 +];
  8 +
  9 +export const formSchema: BFormSchema[] = [
  10 + {
  11 + field: 'name',
  12 + label: t('archive.type.equipmentTypeName'),
  13 + component: 'Input',
  14 + colProps: { span: 24 },
  15 + required: true,
  16 + componentProps: {
  17 + maxLength: 20,
  18 + },
  19 + },
  20 + {
  21 + field: 'code',
  22 + label: t('archive.type.equipmentTypeCode'),
  23 + component: 'Input',
  24 + colProps: { span: 24 },
  25 + required: true,
  26 + componentProps: {
  27 + maxLength: 20,
  28 + },
  29 + },
  30 + {
  31 + field: 'status',
  32 + component: 'Select',
  33 + label: t('archive.type.status'),
  34 + required: true,
  35 + colProps: { span: 24 },
  36 + componentProps: {
  37 + options: statusOptions,
  38 + },
  39 + },
  40 + {
  41 + field: 'notes',
  42 + label: t('equipment.ledger.description'),
  43 + component: 'InputTextArea',
  44 + colProps: { span: 24 },
  45 + componentProps: {
  46 + maxLength: 200,
  47 + },
  48 + },
  49 +];
... ...
  1 +export enum SchemaFiled {
  2 + WAY = 'queryMode',
  3 + TIME_PERIOD = 'timePeriod',
  4 + KEYS = 'keys',
  5 + DATE_RANGE = 'dataRange',
  6 + START_TS = 'startTs',
  7 + END_TS = 'endTs',
  8 + INTERVAL = 'interval',
  9 + LIMIT = 'limit',
  10 + AGG = 'agg',
  11 + ORDER_BY = 'orderBy',
  12 + DATA_TYPE = 'dataType',
  13 +}
... ...
  1 +import { BasicColumn } from '/@/components/Table';
  2 +import { FormSchema } from '/@/components/Table';
  3 +import { h } from 'vue';
  4 +import { Tooltip } from 'ant-design-vue';
  5 +import { handeleCopy } from '../../../device/profiles/step/topic';
  6 +import { useI18n } from '/@/hooks/web/useI18n';
  7 +import { Tag } from 'ant-design-vue';
  8 +const { t } = useI18n();
  9 +
  10 +export enum TypeListAuthEnum {
  11 + /**
  12 + * @description 新增
  13 + */
  14 + CREATE = 'api:yt:archive:type:post',
  15 +
  16 + /**
  17 + * @description 删除
  18 + */
  19 + DELETE = 'api:yt:archive:type:delete',
  20 +
  21 + /**
  22 + * @description 编辑
  23 + */
  24 + UPDATE = 'api:yt:archive:type:update',
  25 +
  26 + /**
  27 + * @description 启用
  28 + */
  29 + ENABLE = 'api:yt:archive:type:enable',
  30 +
  31 + /**
  32 + * @description 停用
  33 + */
  34 + STOP = 'api:yt:archive:type:stop',
  35 +}
  36 +
  37 +// 表格列数据
  38 +export const columns: BasicColumn[] = [
  39 + {
  40 + dataIndex: 'name',
  41 + title: t('archive.type.equipmentTypeName'),
  42 + width: 210,
  43 + slots: { customRender: 'name', title: 'deviceTitle' },
  44 + className: 'type-name-edge',
  45 + customRender: ({ record }) => {
  46 + return h('div', { class: 'py-3 px-3.5' }, [
  47 + h(
  48 + 'div',
  49 + {
  50 + class: 'cursor-pointer text-blue-500 truncate',
  51 + onClick: () => {
  52 + handeleCopy(`${record.name}`);
  53 + },
  54 + },
  55 + h(
  56 + Tooltip,
  57 + {
  58 + placement: 'topLeft',
  59 + title: `${record.name}`,
  60 + },
  61 + () => `${record.name}`
  62 + )
  63 + ),
  64 + ]);
  65 + },
  66 + },
  67 + {
  68 + title: t('archive.type.equipmentTypeCode'),
  69 + dataIndex: 'code',
  70 + width: 100,
  71 + },
  72 + {
  73 + title: t('archive.type.status'),
  74 + dataIndex: 'status',
  75 + width: 110,
  76 + customRender: ({ record }) => {
  77 + const color = record?.status == 'ENABLE' ? 'blue' : 'red';
  78 + const text = record.status == 'ENABLE' ? t('common.enableText') : t('common.stop');
  79 + return h(Tag, { color: color }, () => text);
  80 + },
  81 + },
  82 + {
  83 + title: t('archive.type.remark'),
  84 + dataIndex: 'notes',
  85 + width: 120,
  86 + },
  87 + {
  88 + title: t('archive.type.creator'),
  89 + dataIndex: 'creatorName',
  90 + width: 120,
  91 + },
  92 + {
  93 + title: t('archive.type.createTime'),
  94 + dataIndex: 'createTime',
  95 + width: 120,
  96 + },
  97 +];
  98 +
  99 +// 查询字段
  100 +export const searchFormSchema: FormSchema[] = [
  101 + {
  102 + field: 'name',
  103 + label: t('archive.type.equipmentTypeCodeOrName'),
  104 + component: 'Input',
  105 + colProps: { span: 6 },
  106 + componentProps: {
  107 + maxLength: 255,
  108 + },
  109 + },
  110 + // {
  111 + // field: 'code',
  112 + // label: t('archive.type.equipmentTypeCode'),
  113 + // component: 'Input',
  114 + // colProps: { span: 6 },
  115 + // componentProps: {
  116 + // maxLength: 255,
  117 + // },
  118 + // },
  119 +];
... ...
  1 +<template>
  2 + <div>
  3 + <PageWrapper dense contentFullHeight contentClass="flex">
  4 + <BasicTable style="flex: auto" @register="registerTable" class="w-5/6 xl:w-4/5 type-table">
  5 + <template #toolbar>
  6 + <Authority :value="TypeListAuthEnum.CREATE">
  7 + <a-button
  8 + type="primary"
  9 + @click="handleBussinessDrawer('add', null)"
  10 + v-if="authBtn(role)"
  11 + >
  12 + {{ t('common.createText') }}
  13 + </a-button>
  14 + </Authority>
  15 + <Authority :value="TypeListAuthEnum.DELETE">
  16 + <Popconfirm :title="t('common.deleteConfirmText')" @click="handleDelete()">
  17 + <a-button color="error" :disabled="!isExistOption">
  18 + {{ t('common.batchDeleteText') }}
  19 + </a-button>
  20 + </Popconfirm>
  21 + </Authority>
  22 + <Authority :value="TypeListAuthEnum.ENABLE">
  23 + <a-button
  24 + @click="handleChangeStatus('enable')"
  25 + v-if="authBtn(role)"
  26 + :disabled="!isExistOption"
  27 + >
  28 + {{ t('common.enableText') }}
  29 + </a-button>
  30 + </Authority>
  31 + <Authority :value="TypeListAuthEnum.STOP">
  32 + <a-button
  33 + @click="handleChangeStatus('stop')"
  34 + v-if="authBtn(role)"
  35 + :disabled="!isExistOption"
  36 + >
  37 + {{ t('common.stop') }}
  38 + </a-button>
  39 + </Authority>
  40 + </template>
  41 + <!-- <template #status="{ record }"> -->
  42 + <!-- <Tag
  43 + :color="
  44 + record.status == SbStatusEnum.SCRAP
  45 + ? 'warning'
  46 + : record.status == SbStatusEnum.NORMAL
  47 + ? 'success'
  48 + : record.status == SbStatusEnum.FAULT
  49 + ? 'error'
  50 + : 'error'
  51 + "
  52 + class="ml-2"
  53 + >
  54 + {{ t(`enum.sbStatus.${record.status}`) }}
  55 + </Tag> -->
  56 + <!-- </template> -->
  57 + <template #action="{ record }">
  58 + <TableAction
  59 + :actions="[
  60 + // {
  61 + // label: t('common.viewText'),
  62 + // auth: 'api:yt:ledger:get',
  63 + // icon: 'ant-design:eye-outlined',
  64 + // onClick: handleBussinessDrawer.bind(null, 'view', record),
  65 + // },
  66 + {
  67 + label: t('common.editText'),
  68 + auth: TypeListAuthEnum.UPDATE,
  69 + icon: 'clarity:note-edit-line',
  70 + onClick: handleBussinessDrawer.bind(null, 'edit', record),
  71 + },
  72 + {
  73 + label: t('common.delText'),
  74 + icon: 'ant-design:delete-outlined',
  75 + auth: TypeListAuthEnum.DELETE,
  76 + color: 'error',
  77 + popConfirm: {
  78 + title: t('common.isDelete'),
  79 + confirm: handleDelete.bind(null, record),
  80 + },
  81 + },
  82 + {
  83 + label: t('common.stop'),
  84 + auth: TypeListAuthEnum.STOP,
  85 + icon: 'ant-design:check-circle-outlined',
  86 + onClick: handleChangeStatus.bind(null, 'stop', record),
  87 + ifShow: record.status === 'ENABLE',
  88 + },
  89 + {
  90 + label: t('common.enableText'),
  91 + auth: TypeListAuthEnum.ENABLE,
  92 + icon: 'ant-design:close-circle-outlined',
  93 + ifShow: record.status === 'STOP',
  94 + onClick: handleChangeStatus.bind(null, 'enable', record),
  95 + },
  96 + ]"
  97 + />
  98 + </template>
  99 + </BasicTable>
  100 + <TypeDrawer @register="registerDrawer" @success="handleSuccess" @reload="handleSuccess" />
  101 + </PageWrapper>
  102 + </div>
  103 +</template>
  104 +<script setup lang="ts">
  105 + import { reactive } from 'vue';
  106 + import { PageWrapper } from '/@/components/Page';
  107 + import { TypeDrawer } from './components/modal/index';
  108 + const searchInfo = reactive<Recordable>({});
  109 + import { BasicTable, TableAction, useTable } from '/@/components/Table';
  110 + import {
  111 + getEquipmentTypeList,
  112 + deleteEquipmentType,
  113 + updateEquipmentTypeStatus,
  114 + } from '/@/api/archive/type';
  115 + import { columns, searchFormSchema, TypeListAuthEnum } from './config/type.data';
  116 + import { useBatchOperation } from '/@/utils/useBatchOperation';
  117 + import { useI18n } from '/@/hooks/web/useI18n';
  118 + import { authBtn } from '/@/enums/roleEnum';
  119 + import { Authority } from '/@/components/Authority';
  120 + import { getAuthCache } from '/@/utils/auth';
  121 + import { USER_INFO_KEY } from '/@/enums/cacheEnum';
  122 + import { useMessage } from '/@/hooks/web/useMessage';
  123 +
  124 + import { useDrawer } from '/@/components/Drawer';
  125 +
  126 + // 业务弹窗
  127 + const [registerDrawer, { openDrawer }] = useDrawer();
  128 + const { t } = useI18n();
  129 + const userInfo: any = getAuthCache(USER_INFO_KEY);
  130 + const role: string = userInfo.roles[0];
  131 +
  132 + const { createMessage } = useMessage();
  133 +
  134 + const [
  135 + registerTable,
  136 + { reload, setLoading, getSelectRowKeys, setSelectedRowKeys, getRowSelection },
  137 + ] = useTable({
  138 + title: t('archive.type.typeListText'),
  139 + api: getEquipmentTypeList,
  140 + columns,
  141 + beforeFetch: (params) => {
  142 + const { deviceProfileId } = params;
  143 + if (!deviceProfileId) return;
  144 + const obj = {
  145 + ...params,
  146 + ...{
  147 + deviceProfileIds: deviceProfileId ? [deviceProfileId] : null,
  148 + },
  149 + };
  150 + delete obj.deviceProfileId;
  151 + return obj;
  152 + },
  153 + formConfig: {
  154 + labelWidth: 140,
  155 + schemas: searchFormSchema,
  156 + },
  157 + useSearchForm: true,
  158 + showTableSetting: true,
  159 + bordered: true,
  160 + showIndexColumn: false,
  161 + rowKey: 'id',
  162 + searchInfo: searchInfo,
  163 + clickToRowSelect: false,
  164 + rowClassName: (record) => ((record as any).alarmStatus ? 'device-alarm-badge' : ''),
  165 + rowSelection: { type: 'checkbox' },
  166 + actionColumn: {
  167 + width: 200,
  168 + title: t('common.actionText'),
  169 + slots: { customRender: 'action' },
  170 + fixed: 'right',
  171 + },
  172 + });
  173 +
  174 + const { isExistOption } = useBatchOperation(getRowSelection, setSelectedRowKeys);
  175 +
  176 + // 业务弹窗
  177 + const handleBussinessDrawer = (text, record) => {
  178 + const modalParams = {
  179 + text,
  180 + record,
  181 + };
  182 + openDrawer(true, modalParams);
  183 + };
  184 +
  185 + // 删除
  186 + const handleDelete = async (record?: any) => {
  187 + // eslint-disable-next-line no-console
  188 + console.log(record, '这是一条临时调试用的日志');
  189 + let ids: string[] = [];
  190 + if (record) {
  191 + ids = [record.id];
  192 + } else {
  193 + ids = getSelectRowKeys();
  194 + }
  195 + try {
  196 + setLoading(true);
  197 + await deleteEquipmentType({ ids });
  198 + createMessage.success(t('common.deleteSuccessText'));
  199 + handleReload();
  200 + } catch (error) {
  201 + throw error;
  202 + } finally {
  203 + setLoading(false);
  204 + }
  205 + };
  206 +
  207 + // 启用 停用
  208 + const handleChangeStatus = async (type: 'enable' | 'stop', record?: any) => {
  209 + let ids: string[] = [];
  210 + if (record) {
  211 + ids = [record?.id];
  212 + } else {
  213 + ids = getSelectRowKeys();
  214 + }
  215 + if (ids?.length)
  216 + try {
  217 + setLoading(true);
  218 + if (type === 'enable') {
  219 + await updateEquipmentTypeStatus({ ids, status: 'ENABLE' });
  220 + } else {
  221 + await updateEquipmentTypeStatus({ ids, status: 'STOP' });
  222 + }
  223 + createMessage.success(t('common.editSuccessText'));
  224 + handleReload();
  225 + } catch (error) {
  226 + throw error;
  227 + } finally {
  228 + setLoading(false);
  229 + }
  230 + };
  231 +
  232 + // 重置
  233 + function handleReload() {
  234 + setSelectedRowKeys([]);
  235 + handleSuccess();
  236 + }
  237 +
  238 + function handleSuccess() {
  239 + reload();
  240 + }
  241 +</script>
  242 +
  243 +<style scoped lang="less">
  244 + .type-table {
  245 + :deep(.ant-form-item-control-input-content) {
  246 + & > div > div {
  247 + width: 100%;
  248 + }
  249 + }
  250 + }
  251 +</style>
  252 +
  253 +<style lang="less">
  254 + .type-status {
  255 + position: relative;
  256 +
  257 + .type-collect {
  258 + width: 0;
  259 + height: 0;
  260 + border-top: 30px solid #377dff;
  261 + border-right: 30px solid transparent;
  262 + }
  263 + }
  264 +
  265 + .type-name-edge {
  266 + width: 100%;
  267 + height: 100%;
  268 + padding: 0 !important;
  269 + position: relative;
  270 + }
  271 +</style>
... ...