Explorar el Código

重构vxetable(formconfig待办)-- layout 菜单

zhuangyunsheng hace 3 días
padre
commit
f0ef119799
Se han modificado 38 ficheros con 476 adiciones y 332 borrados
  1. 1 1
      src/components/scCodeEditor/index.vue
  2. 1 1
      src/components/scEcharts/index.vue
  3. 6 5
      src/components/scFormTable/detail.vue
  4. 5 4
      src/components/scFormTable/index.vue
  5. 1 1
      src/components/scImage/index.vue
  6. 24 22
      src/components/scTable/index.vue
  7. 2 2
      src/components/scTable/renderer/cell-tag.vue
  8. 2 2
      src/components/scTable/renderer/form-radio.vue
  9. 2 2
      src/components/scTable/renderer/form-select.vue
  10. 2 2
      src/components/scTable/renderer/pager-batch-del.vue
  11. 2 2
      src/components/scTable/renderer/table-search.vue
  12. 4 3
      src/components/scTableInput/index.vue
  13. 1 1
      src/components/scUpload/file.vue
  14. 1 1
      src/components/scUpload/index.vue
  15. 1 1
      src/components/scVideo/index.vue
  16. 209 0
      src/layout/index copy.vue
  17. 17 15
      src/layout/index.vue
  18. 2 1
      src/utils/basicDic.js
  19. 27 17
      src/views/basic/customer/index.vue
  20. 2 1
      src/views/basic/material/detail.vue
  21. 26 17
      src/views/basic/material/index.vue
  22. 1 4
      src/views/basic/qualityPlan/index.vue
  23. 1 1
      src/views/production/bom/detail.vue
  24. 9 16
      src/views/production/bom/index.vue
  25. 10 14
      src/views/production/bom/main.js
  26. 1 1
      src/views/sales/order/detail.vue
  27. 2 5
      src/views/sales/order/index.vue
  28. 7 14
      src/views/sales/order/main.js
  29. 27 35
      src/views/sales/plan/index.vue
  30. 23 47
      src/views/system/dept/index.vue
  31. 3 5
      src/views/system/role/index.vue
  32. 5 10
      src/views/system/user/index.vue
  33. 3 10
      src/views/userCenter/index.vue
  34. 1 1
      src/views/workmanship/line/desc.vue
  35. 5 35
      src/views/workmanship/line/history.vue
  36. 15 9
      src/views/workmanship/line/index.vue
  37. 1 7
      src/views/workmanship/line/main.js
  38. 24 17
      src/views/workmanship/process/index.vue

+ 1 - 1
src/components/scCodeEditor/index.vue

@@ -29,7 +29,7 @@ export default {
         modelValue: { type: String, default: "" },
         mode: { type: String, default: "javascript" },
         height: { type: [String, Number], default: 300 },
-        options: { type: Object, default: () => {} },
+        options: { type: Object, default: () => ({}) },
         theme: { type: String, default: "neat" },
         readOnly: { type: Boolean, default: false }
     },

+ 1 - 1
src/components/scEcharts/index.vue

@@ -15,7 +15,7 @@
 			height: { type: String, default: "100%" },
 			width: { type: String, default: "100%" },
 			nodata: { type: Boolean, default: false },
-			option: { type: Object, default: () => {} },
+			option: { type: Object, default: () => ({}) },
 			clearCache: { type: Boolean, default: false }
 		},
 		data() {

+ 6 - 5
src/components/scFormTable/detail.vue

@@ -1,6 +1,6 @@
 <template>
     <el-dialog v-model="visible" :title="compDic[tableKey].title" :width="1000" append-to-body :close-on-click-modal="false" @closed="$emit('closed')">
-        <component v-if="tableKey" :is="compDic[tableKey].compName" ref="tableRef" hidePageHeader hideHandler :hideCheckbox="!multiple" :options="tableOptions" />
+        <component v-if="tableKey" :is="compDic[tableKey].compName" ref="tableRef" selectable :multiple="multiple" :paramsColums="paramsColums" :options="tableOptions" />
         
         <template #footer>
             <el-button auto-insert-space @click="visible = false">取消</el-button>
@@ -15,13 +15,13 @@ import processTable from "@/views/workmanship/process/index";
 import materialTable from "@/views/basic/material/index";
 import customerTable from "@/views/basic/customer/index";
 
+const $emit = defineEmits(["success", "closed"]);
 const props = defineProps({
     tableKey: { type: String, default: "" },
     multiple: { type: Boolean, default: false },
-    options: { type: Object, default: () => {} }
+    paramsColums: { type: Array, default: () => [] },
+    options: { type: Object, default: () => ({}) }
 });
-const $emit = defineEmits(["success", "closed"]);
-const visible = ref(false);
 
 const compDic = reactive({
     stage: { title: "工序选择", compName: processTable },
@@ -29,7 +29,6 @@ const compDic = reactive({
     customer: { title: "客户选择", compName: customerTable },
 });
 
-const tableRef = ref();
 const tableOptions = reactive({
     checkedRows: [],
     maxHeight: 1048,
@@ -37,6 +36,8 @@ const tableOptions = reactive({
     ...props.options
 });
 
+const visible = ref(false);
+const tableRef = ref();
 const setData = data => {
     visible.value = true;
     tableOptions.checkedRows = data;

+ 5 - 4
src/components/scFormTable/index.vue

@@ -55,16 +55,16 @@ const props = defineProps({
     modelValue: { type: Array, default: () => [] },
     disabled: { type: Boolean, default: false },
     tableKey: { type: String, default: "" },
-    addTemplate: { type: Object, default: () => {} },
+    addTemplate: { type: Object, default: () => ({}) },
     
     layouts: { type: Array, default: () => [["Top", "Form"], ["Toolbar", "Table", "Bottom", "Pager"]] },
     rowKey: { type: String, default: "id" },
     columns: { type: Array, default: () => [] },
-    editRules: { type: Object, default: () => {} },
+    editRules: { type: Object, default: () => ({}) },
     footerField: { type: Array, default: () => [] },
     mergeFooterItems: { type: Array, default: () => [] },
 
-    selectOptions: { type: Object, default: () => {} },
+    selectOptions: { type: Object, default: () => ({}) },
     add_success: { type: Function, default: (oldValue, newValue) => newValue }
 });
 
@@ -112,9 +112,10 @@ const gridOptions = reactive({
 const selectTableOptions = reactive({
     tableKey: props.tableKey,
     multiple: true,
+    paramsColums: XEUtils.get(props.selectOptions, "paramsColums", []),
     options: {
         rowKey: props.rowKey,
-        ...props.selectOptions
+        ...XEUtils.omit(props.selectOptions, "paramsColums")
     }
 });
 

+ 1 - 1
src/components/scImage/index.vue

@@ -14,7 +14,7 @@ import { VxeUI } from "vxe-pc-ui";
 import API from "@/api";
 
 const props = defineProps({
-    file: { type: Object, default: () => {} }
+    file: { type: Object, default: () => ({}) }
 })
 
 const formatUrl = computed(() => props.file.path.startsWith("/process") ? "/minio" + props.file.path : "data:image/jpeg;base64," + props.file.path);

+ 24 - 22
src/components/scTable/index.vue

@@ -1,7 +1,6 @@
 <!--
  * @Descripttion: 数据表格组件
  * @version: 1.10
- * 待重构
 -->
 
 <template>
@@ -30,28 +29,26 @@ import XEUtils from "xe-utils";
 import store from "@/store";
 import config from "@/config/table";
 import pagerBatchDel from "./renderer/pager-batch-del";
+import { watch, watchEffect } from "vue";
 
 const props = defineProps({
-    apiObj: { type: Object, default: () => {} },
+    apiObj: { type: Object, default: () => ({}) },
     apiKey: { type: String, default: () => "get" },
     rowKey: { type: String, default: "id" },
     minHeight: { type: [String, Number], default: VxeUI.getConfig().table.minHeight || 144 },
     maxHeight: { type: [String, Number] },
     layouts: { type: Array, default: () => [["Top", "Form"], ["Toolbar", "Table", "Bottom", "Pager"]] },
     checkedRows: { type: Array, default: () => [] },
-    /* ***************  query  *************** */ 
-    formConfig: { type: Object, default: () => {} },
+    formConfig: { type: Object, default: () => ({}) },
     paramsColums: { type: Array, default: () => [] },
-    /* ***************  query  *************** */ 
-    toolbarConfig: { type: Object, default: () => {} },
-    editConfig: { type: Object, default: () => {} },
+    toolbarConfig: { type: Object, default: () => ({}) },
+    editConfig: { type: Object, default: () => ({}) },
+    treeConfig: { type: Object, default: null },
     columns: { type: Array, default: () => [] },
-    /* ***************  pager  *************** */ 
-    pagerConfig: { type: Object, default: () => {} },
+    pagerConfig: { type: Object, default: () => ({}) },
     batchDel: { type: Boolean, default: () => false },
-    /* ***************  pager  *************** */ 
-    options: { type: Object, default: () => {} }
-})
+    options: { type: Object, default: () => ({}) }
+});
 
 const xGrid = ref();
 const gridOptions = reactive({
@@ -65,7 +62,7 @@ const gridOptions = reactive({
     columns: props.columns,
     showOverflow: true,
     keepSource: true,
-    layouts: [...props.layouts],
+    layouts: props.layouts,
     formConfig: {
         enabled: true,
         className: "vxe-table-query",
@@ -106,7 +103,7 @@ const gridOptions = reactive({
                 }
             }
         ],
-        ...XEUtils.omit(XEUtils.get(props, "formConfig", {}), "items")
+        ...XEUtils.omit(props.formConfig, "items")
     },
     toolbarConfig: {
         enabled: false,
@@ -132,11 +129,9 @@ const gridOptions = reactive({
     },
     printConfig: {},
     importConfig: {
-        // remote: true,
         types: ["xlsx", "xls"],
         mode: "insertBottom",
-        modes: ["insertBottom", "insertTop", "covering"],
-        // importMethod: ({ $grid, options }) => {},
+        modes: ["insertBottom", "insertTop", "covering"]
     },
     exportConfig: {
         types: ["xlsx"],
@@ -160,14 +155,14 @@ const gridOptions = reactive({
     },
     radioConfig: {
         reserve: true, // 是否保留勾选状态
-        highlight: true,
+        highlight: true
     },
     checkboxConfig: {
         reserve: true, // 是否保留勾选状态
         highlight: true,
         // 树结构有冲突
-        range: computed(() => !XEUtils.has(props.options, "treeConfig")), // 鼠标在复选框的列内滑动选中或取消指定行
-        isShiftKey: computed(() => !XEUtils.has(props.options, "treeConfig")) // 鼠标点击和 shift 键选取指定范围的行
+        range: computed(() => !props.treeConfig), // 鼠标在复选框的列内滑动选中或取消指定行
+        isShiftKey: computed(() => !props.treeConfig) // 鼠标点击和 shift 键选取指定范围的行
     },
     tooltipConfig: {
         enterable: true
@@ -175,6 +170,7 @@ const gridOptions = reactive({
     expandConfig: {
         padding: true
     },
+    treeConfig: props.treeConfig,
     editConfig: {
         enabled: false,
         mode: "cell",
@@ -195,9 +191,15 @@ const gridOptions = reactive({
         ...props.pagerConfig
     },
     ...props.options
+});
+
+watchEffect(() => {
+    XEUtils.objectEach(props.options, (value, key) => {
+        if (XEUtils.has(gridOptions, key) && (XEUtils.isObject(value) || XEUtils.isArray(value))) XEUtils.merge(gridOptions[key], value);
+        else XEUtils.set(gridOptions, key, value);
+    });
 })
 
-watch(() => props.options, val => XEUtils.merge(gridOptions, val), { deep: true });
 watch(() => gridOptions.data, val => {
     if (props.columns.find(item => item.type == "checkbox" && XEUtils.get(item, "visible", true)) && props.checkedRows.length) {
         xGrid.value?.setCheckboxRow(props.checkedRows, true);
@@ -226,7 +228,7 @@ const resizeTable = () => {
 // 获取数据
 const getData = () => {
     nextTick(() => {
-        if (!props.apiObj) {
+        if (!props.apiObj || XEUtils.isEmpty(props.apiObj)) {
             if (props.options.data && props.options.data.length > 0) {
                 gridOptions.pagerConfig.total = props.options.data.length;
                 return;

+ 2 - 2
src/components/scTable/renderer/cell-tag.vue

@@ -18,8 +18,8 @@ const colorDic = {
 }
 
 const props = defineProps({
-    renderOpts: { type: Object, default: () => {} },
-    params: { type: Object, default: () => {} }
+    renderOpts: { type: Object, default: () => ({}) },
+    params: { type: Object, default: () => ({}) }
 })
 
 const modelValue = computed(() => XEUtils.get(props, "renderOpts.defaultValue", null));

+ 2 - 2
src/components/scTable/renderer/form-radio.vue

@@ -9,8 +9,8 @@ import XEUtils from "xe-utils";
 import config from "@/config/select";
 
 const props = defineProps({
-    renderOpts: { type: Object, default: () => {} },
-    params: { type: Object, default: () => {} }
+    renderOpts: { type: Object, default: () => ({}) },
+    params: { type: Object, default: () => ({}) }
 })
 
 const modelValue = ref(null);

+ 2 - 2
src/components/scTable/renderer/form-select.vue

@@ -17,8 +17,8 @@ import TOOL from "@/utils/tool";
 import config from "@/config/select";
 
 const props = defineProps({
-    renderOpts: { type: Object, default: () => {} },
-    params: { type: Object, default: () => {} }
+    renderOpts: { type: Object, default: () => ({}) },
+    params: { type: Object, default: () => ({}) }
 })
 
 watch(props.params.data, e => {

+ 2 - 2
src/components/scTable/renderer/pager-batch-del.vue

@@ -9,8 +9,8 @@ import XEUtils from "xe-utils";
 
 const $emit = defineEmits(["success"]);
 const props = defineProps({
-    params: { type: Object, default: () => {} },
-    apiObj: { type: Object, default: () => {} }
+    params: { type: Object, default: () => ({}) },
+    apiObj: { type: Object, default: () => ({}) }
 })
 
 const ids = ref([]);

+ 2 - 2
src/components/scTable/renderer/table-search.vue

@@ -8,8 +8,8 @@
 import XEUtils from "xe-utils";
 
 const props = defineProps({
-    renderOpts: { type: Object, default: () => {} },
-    params: { type: Object, default: () => {} }
+    renderOpts: { type: Object, default: () => ({}) },
+    params: { type: Object, default: () => ({}) }
 })
 
 const tableSearch = ref({

+ 4 - 3
src/components/scTableInput/index.vue

@@ -16,7 +16,7 @@
         </el-input>
     </div>
 
-    <select-table v-if="dialog" ref="selectTableRef" v-bind="{ tableKey, options }" @success="success" @closed="dialog = false"></select-table>
+    <select-table v-if="dialog" ref="selectTableRef" :tableKey="tableKey" :paramsColums="paramsColums" :options="options" @success="success" @closed="dialog = false"></select-table>
 </template>
 
 <script setup>
@@ -25,11 +25,12 @@ import selectTable from "@/components/scFormTable/detail";
 
 const $emit = defineEmits(["update:modelValue"]);
 const props = defineProps({
-    modelValue: { type: Object, default: () => {} },
+    modelValue: { type: Object, default: () => ({}) },
     valueKey: { type: String, default: () => "id" },
     tableKey: { type: String, default: () => "" },
     hideShow: { type: Boolean, default: () => false },
-    options: { type: Object, default: () => {} }
+    paramsColums: { type: Array, default: () => [] },
+    options: { type: Object, default: () => ({}) }
 });
 
 const dialog = ref(false);

+ 1 - 1
src/components/scUpload/file.vue

@@ -50,7 +50,7 @@ export default {
         disabled: { type: Boolean, default: false },
         hideAdd: { type: Boolean, default: false },
         onSuccess: { type: Function, default: () => { return true } },
-        params: { type: Object, default: () => {} }
+        params: { type: Object, default: () => ({}) }
     },
 
     data() {

+ 1 - 1
src/components/scUpload/index.vue

@@ -66,7 +66,7 @@ import { fileTypes } from "@/components/scUpload/main";
 
 export default {
     props: {
-        modelValue: { type: Object, default: () => {} },
+        modelValue: { type: Object, default: () => ({}) },
         width: { type: Number, default: 148 },
         height: { type: Number, default: 148 },
         title: { type: String, default: "" },

+ 1 - 1
src/components/scVideo/index.vue

@@ -24,7 +24,7 @@
 			controls: { type: Boolean, default: true },
 			loop: { type: Boolean, default: false },
 			isLive: { type: Boolean, default: false },
-			options: { type: Object, default: () => {} }
+			options: { type: Object, default: () => ({}) }
 		},
 
 		data() {

+ 209 - 0
src/layout/index copy.vue

@@ -0,0 +1,209 @@
+<template>
+	<!-- 通栏布局 -->
+	<template v-if="layout == 'header'">
+        <header class="aminui-header">
+            <div class="aminui-header-left">
+                <div class="logo-bar">
+                    <img class="logo" :src="require('/public/img/logo.png')">
+                    <span>{{ $CONFIG.APP_NAME }}</span>
+                </div>
+                <el-tabs v-if="!ismobile" ref="menuTabs" class="nav-menu" v-model="tabsName" @tab-change="showMenu">
+                    <el-tab-pane v-for="item in menus" :key="item" :name="item.path">
+                        <template #label>
+                            <sc-iconify :icon="item.meta.icon || undefined"></sc-iconify>
+                            <vxe-text-ellipsis :title="item.meta.title" :content="item.meta.title"></vxe-text-ellipsis>
+                        </template>
+                    </el-tab-pane>
+                </el-tabs>
+            </div>
+            <div class="aminui-header-right">
+                <userbar></userbar>
+            </div>
+        </header>
+        <section class="aminui-wrapper">
+            <div v-if="!ismobile && currentMenu?.children?.length" :class="['aminui-side', menuIsCollapse && 'isCollapse']">
+                <div v-if="!menuIsCollapse" class="aminui-side-top">
+                    <h2><vxe-text-ellipsis :title="currentMenu.meta.title" :content="currentMenu.meta.title"></vxe-text-ellipsis></h2>
+                </div>
+                <div class="aminui-side-scroll">
+                    <el-scrollbar>
+                        <el-menu :default-active="$route.fullPath" router :collapse="menuIsCollapse" :unique-opened="$CONFIG.MENU_UNIQUE_OPENED">
+                            <NavMenu :navMenus="menus.find(menu => menu.path == currentMenu.path).children"></NavMenu>
+                        </el-menu>
+                    </el-scrollbar>
+                </div>
+
+                <div class="aminui-side-collapse" @click="$store.commit('TOGGLE_menuIsCollapse')">
+                    <sc-iconify :icon="menuIsCollapse ? 'ep:arrow-right': 'ep:arrow-left'" size="14"></sc-iconify>
+                </div>
+            </div>
+            <Side-m v-if="ismobile"></Side-m>
+            <div class="aminui-body el-container">
+                <Tags v-if="!ismobile && layoutTags"></Tags>
+                <div class="aminui-main" id="aminui-main">
+                    <router-view v-slot="{ Component }">
+                        <keep-alive :include="this.$store.state.keepAlive.keepLiveRoute">
+                            <component v-if="$store.state.keepAlive.routeShow" :is="Component" :key="$route.fullPath" />
+                            <page-loading v-else />
+                        </keep-alive>
+                    </router-view>
+                </div>
+            </div>
+        </section>
+	</template>
+
+	<!-- 经典布局 -->
+	<template v-else-if="layout == 'menu'">
+        <header class="aminui-header">
+			<div class="aminui-header-left">
+				<div class="logo-bar">
+					<img class="logo" :src="require('/public/img/logo.png')">
+                    <span>{{ $CONFIG.APP_NAME }}</span>
+				</div>
+			</div>
+			<div class="aminui-header-right">
+				<userbar></userbar>
+			</div>
+		</header>
+		<section class="aminui-wrapper">
+			<div v-if="!ismobile" :class="['aminui-side', menuIsCollapse && 'isCollapse']">
+				<div class="aminui-side-scroll">
+					<el-scrollbar>
+						<el-menu :default-active="$route.fullPath" router :collapse="menuIsCollapse" :unique-opened="$CONFIG.MENU_UNIQUE_OPENED">
+							<NavMenu :navMenus="menus"></NavMenu>
+						</el-menu>
+					</el-scrollbar>
+				</div>
+			</div>
+			<Side-m v-if="ismobile"></Side-m>
+			<div class="aminui-body el-container">
+				<Topbar v-if="!ismobile"></Topbar>
+				<Tags v-if="!ismobile && layoutTags"></Tags>
+				<div class="aminui-main" id="aminui-main">
+					<router-view v-slot="{ Component }">
+					    <keep-alive :include="this.$store.state.keepAlive.keepLiveRoute">
+                            <component v-if="$store.state.keepAlive.routeShow" :is="Component" :key="$route.fullPath" />
+                            <page-loading v-else />
+					    </keep-alive>
+					</router-view>
+				</div>
+			</div>
+		</section>
+	</template>
+
+	<!-- 默认布局 -->
+	<template v-else>
+        <section class="aminui-wrapper">
+            <div v-if="!ismobile" :class="['aminui-side hasMenu', menuIsCollapse && 'isCollapse']">
+                <div class="aminui-side-top">
+                    <div class="logo-bar">
+                        <img class="logo" :src="require('/public/img/logo.png')">
+                        <vxe-text-ellipsis v-if="!menuIsCollapse" :title="$CONFIG.APP_NAME" :content="$CONFIG.APP_NAME"></vxe-text-ellipsis>
+                    </div>
+                </div>
+                <div class="aminui-side-scroll">
+                    <el-scrollbar>
+                        <el-menu popper-class="side-menu-popper" :default-active="$route.fullPath" router :collapse="menuIsCollapse" :unique-opened="$CONFIG.MENU_UNIQUE_OPENED">
+                            <NavMenu :navMenus="menus"></NavMenu>
+                        </el-menu>
+                    </el-scrollbar>
+                </div>
+            </div>
+            <Side-m v-if="ismobile"></Side-m>
+            <div class="aminui-body el-container">
+				<Topbar>
+				    <userbar></userbar>
+                </Topbar>
+                <Tags v-if="!ismobile && layoutTags"></Tags>
+                <div class="aminui-main" id="aminui-main">
+                    <router-view v-slot="{ Component }">
+                        <keep-alive :include="this.$store.state.keepAlive.keepLiveRoute">
+                            <component v-if="$store.state.keepAlive.routeShow" :is="Component" :key="$route.fullPath" />
+                            <page-loading v-else />
+                        </keep-alive>
+                    </router-view>
+                </div>
+            </div>
+        </section>
+	</template>
+</template>
+
+<script>
+import XEUtils from "xe-utils";
+
+import pageLoading from "./components/pageLoading";
+import SideM from "./components/sideM";
+import Topbar from "./components/topbar";
+import Tags from "./components/tags";
+import NavMenu from "./components/NavMenu";
+import userbar from "./components/userbar";
+import { nextTick } from 'vue';
+
+export default {
+    name: "index",
+    components: { pageLoading, SideM, Topbar, Tags, NavMenu, userbar },
+
+    data() {
+        return {
+            tabsName: ""
+        }
+    },
+
+    watch: {
+        layout: {
+            immediate: true,
+            handler(val) {
+                document.body.setAttribute("data-layout", val)
+            }
+        },
+
+        currentMenu: {
+            handler(val) {
+                this.tabsName = XEUtils.get(val, "path", "")
+            }
+        },
+    },
+
+    computed: {
+        ismobile() {
+            return this.$store.state.global.ismobile
+        },
+        layout() {
+            return this.$store.state.global.layout
+        },
+        layoutTags() {
+            return this.$store.state.global.layoutTags
+        },
+        menuIsCollapse() {
+            return this.$store.state.global.menuIsCollapse
+        },
+        menus() {
+            return (this.$TOOL.data.get("MENU") || []).filter(item => item.path !== this.$CONFIG.DASHBOARD_URL && !item.meta?.hidden)
+        }, 
+        currentMenu() {
+            return XEUtils.first(XEUtils.searchTree(this.menus, item => item.path === this.$route.path)) || {}
+        }
+    },
+
+    created() {
+        this.tabsName = XEUtils.get(this.currentMenu, "path", "")
+        this.onLayoutResize();
+        window.addEventListener("resize", this.onLayoutResize);
+    },
+
+    methods: {
+        onLayoutResize() {          
+            this.$store.commit("SET_ismobile", document.body.clientWidth < 992);
+        },
+
+        // 点击显示
+        showMenu(path) {
+            const currentMenu = XEUtils.first(XEUtils.searchTree(this.menus, item => item.path === path))
+            currentMenu && this.$router.push({ path: currentMenu.redirect || currentMenu.path });
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 17 - 15
src/layout/index.vue

@@ -137,7 +137,6 @@ import Topbar from "./components/topbar";
 import Tags from "./components/tags";
 import NavMenu from "./components/NavMenu";
 import userbar from "./components/userbar";
-import { nextTick } from 'vue';
 
 export default {
     name: "index",
@@ -153,41 +152,39 @@ export default {
         layout: {
             immediate: true,
             handler(val) {
-                document.body.setAttribute("data-layout", val)
+                document.body.setAttribute("data-layout", val);
             }
         },
 
-        currentMenu: {
-            handler(val) {
-                this.tabsName = XEUtils.get(val, "path", "")
-            }
-        },
+        currentMenu() {
+            this.scrollToActiveTab();
+        }
     },
 
     computed: {
         ismobile() {
-            return this.$store.state.global.ismobile
+            return this.$store.state.global.ismobile;
         },
         layout() {
-            return this.$store.state.global.layout
+            return this.$store.state.global.layout;
         },
         layoutTags() {
-            return this.$store.state.global.layoutTags
+            return this.$store.state.global.layoutTags;
         },
         menuIsCollapse() {
-            return this.$store.state.global.menuIsCollapse
+            return this.$store.state.global.menuIsCollapse;
         },
         menus() {
-            return (this.$TOOL.data.get("MENU") || []).filter(item => item.path !== this.$CONFIG.DASHBOARD_URL && !item.meta?.hidden)
+            return (this.$TOOL.data.get("MENU") || []).filter(item => item.path !== this.$CONFIG.DASHBOARD_URL && !item.meta?.hidden);
         }, 
         currentMenu() {
-            return XEUtils.first(XEUtils.searchTree(this.menus, item => item.path === this.$route.path)) || {}
+            return XEUtils.first(XEUtils.searchTree(this.menus, item => item.path === this.$route.path)) || {};
         }
     },
 
     created() {
-        this.tabsName = XEUtils.get(this.currentMenu, "path", "")
         this.onLayoutResize();
+        nextTick(() => this.scrollToActiveTab());
         window.addEventListener("resize", this.onLayoutResize);
     },
 
@@ -196,9 +193,14 @@ export default {
             this.$store.commit("SET_ismobile", document.body.clientWidth < 992);
         },
 
+        scrollToActiveTab() {
+            this.tabsName = XEUtils.get(this.currentMenu, "path", "");
+            this.$refs.menuTabs.tabNavRef.scrollToActiveTab();
+        },
+
         // 点击显示
         showMenu(path) {
-            const currentMenu = XEUtils.first(XEUtils.searchTree(this.menus, item => item.path === path))
+            const currentMenu = XEUtils.first(XEUtils.searchTree(this.menus, item => item.path === path));
             currentMenu && this.$router.push({ path: currentMenu.redirect || currentMenu.path });
         }
     }

+ 2 - 1
src/utils/basicDic.js

@@ -29,7 +29,8 @@ export const materialDic = {
     typeRelation: {
         self_made: ["semi_finished", "finished_product"],
         out_purchase: ["raw_material", "trade_goods", "packaging_material"],
-        outsourcing: ["semi_finished", "finished_product"]
+        outsourcing: ["semi_finished", "finished_product"],
+        salable: ["finished_product", "trade_goods"]
     },
 
     unit: {

+ 27 - 17
src/views/basic/customer/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<el-container class="is-vertical">
-        <sc-page-header v-if="!hidePageHeader" @add="table_add"></sc-page-header>
+        <sc-page-header v-if="!selectable" @add="table_add"></sc-page-header>
 
         <scTable ref="xGridTable" :apiObj="$API.basic.customer" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="options">
             <template #action="{ row }">
@@ -33,10 +33,10 @@ import { mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper
 import customerDetail from "./detail";
 
 const props = defineProps({
-    options: { type: Object, default: () => {} },
-    hidePageHeader: { type: Boolean, default: false },
-    hideHandler: { type: Boolean, default: false },
-    hideCheckbox: { type: Boolean, default: false }
+    selectable: { type: Boolean, default: false },
+    multiple: { type: Boolean, default: false },
+    paramsColums: { type: Array, default: () => [] },
+    options: { type: Object, default: () => ({}) }
 });
 
 const toolbarConfig = reactive({
@@ -53,12 +53,16 @@ const selectConfig = reactive({
 
 const formConfig = reactive({
     data: {},
-    items: [
+    items: computed(() => props.selectable ? [
+        mapFormItemInput("nameLike", "客户名称"),
+        mapFormItemInput("codeLike", "客户编号"),
+        mapFormItemSelect("type", "客户分类", { ...selectConfig, options: objectToArray(customerDic.type) })
+    ] : [
         mapFormItemInput("nameLike", "客户名称"),
         mapFormItemInput("codeLike", "客户编号"),
         mapFormItemSelect("status", "客户状态", selectConfig),
         mapFormItemSelect("type", "客户分类", { ...selectConfig, options: objectToArray(customerDic.type) })
-    ]
+    ])
 });
 
 const paramsColums = reactive([
@@ -67,15 +71,24 @@ const paramsColums = reactive([
     { column: "nameLike" },
     { column: "codeLike" },
     { column: "status" },
-    { column: "type" }
+    { column: "type" },
+    ...props.paramsColums
 ]);
 
-const columns = reactive([
-    { visible: props.hideHandler, type: props.hideCheckbox && "radio" || "checkbox", fixed: "left", width: 40 },
-    { visible: !props.hideHandler, type: "seq", fixed: "left", width: 60 },
+const columns = computed(() => props.selectable ? [
+    { type: props.multiple && "checkbox" || "radio", fixed: "left", width: 40 },
+    { type: "html", field: "name", title: "客户名称", fixed: "left", minWidth: 150, sortable: true },
+    { type: "html", field: "code", title: "客户编号", fixed: "left", minWidth: 150, sortable: true },
+    { type: "html", field: "type", title: "客户分类", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(customerDic.type, cellValue, cellValue) },
+    { type: "html", field: "valueLevel", title: "客户层级", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(customerDic.tier, cellValue, cellValue) },
+    { type: "html", field: "creditLevel", title: "信用等级", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(customerDic.rating, cellValue, cellValue) },
+    { type: "html", field: "managerName", title: "联系人", minWidth: 120, sortable: true },
+    { type: "html", field: "managerPhone", title: "联系方式", minWidth: 120, sortable: true }
+] : [
+    { type: "seq", fixed: "left", width: 60 },
     { type: "html", field: "name", title: "客户名称", fixed: "left", minWidth: 150, sortable: true },
     { type: "html", field: "code", title: "客户编号", fixed: "left", minWidth: 150, sortable: true },
-    { visible: !props.hideHandler, field: "status", title: "客户状态", minWidth: 100, editRender: { name: "$cell-tag", options: statusDic } },
+    { field: "status", title: "客户状态", minWidth: 100, editRender: { name: "$cell-tag", options: statusDic } },
     { type: "html", field: "type", title: "客户分类", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(customerDic.type, cellValue, cellValue) },
     { visible: false, type: "html", field: "valueLevel", title: "客户层级", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(customerDic.tier, cellValue, cellValue) },
     { visible: false, type: "html", field: "creditLevel", title: "信用等级", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(customerDic.rating, cellValue, cellValue) },
@@ -84,15 +97,12 @@ const columns = reactive([
     { visible: false, type: "html", field: "email", title: "邮箱", minWidth: 120, sortable: true },
     { type: "html", field: "createTime", title: "创建日期", minWidth: 120, sortable: true, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue, "YYYY-MM-DD") || cellValue },
     { visible: false, type: "html", field: "address", title: "客户地址", minWidth: 300, sortable: true },
-    { visible: !props.hideHandler, title: "操作", fixed: "right", width: 220, slots: { default: "action" } }
+    { title: "操作", fixed: "right", width: 220, slots: { default: "action" } }
 ]);
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const customerRef = ref();
 const dialog = ref(false);

+ 2 - 1
src/views/basic/material/detail.vue

@@ -43,7 +43,7 @@
                                 </el-select>
                             </el-form-item>
                         </el-col>
-                        <el-col :md="8" :xs="24">
+                        <el-col v-if="isSalable" :md="8" :xs="24">
                             <el-form-item label="标准售价" prop="price">
                                 <el-input-number v-model="form.price" :min="0" :precision="2" :controls="false" placeholder="请输入标准售价">
                                     <template #suffix>元</template>
@@ -110,6 +110,7 @@ const rules = reactive({
     needType: [{ required: true, message: "请选择物料来源" }]
 });
 
+const isSalable = computed(() => form.value.materialType && XEUtils.includes(materialDic.typeRelation["salable"], form.value.materialType));
 const needTypeOptionDisabled = key => form.value.materialType && !XEUtils.includes(materialDic.typeRelation[key], form.value.materialType);
 const materialOptionDisabled = key => form.value.needType && !XEUtils.includes(XEUtils.get(materialDic.typeRelation, form.value.needType, []), key);
 

+ 26 - 17
src/views/basic/material/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<el-container class="is-vertical">
-        <sc-page-header v-if="!hidePageHeader" @add="table_add"></sc-page-header>
+        <sc-page-header v-if="!selectable" @add="table_add"></sc-page-header>
 
         <scTable ref="xGridTable" :apiObj="$API.basic.material" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="options">
             <template #action="{ row }">
@@ -34,10 +34,10 @@ import { mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper
 import materialDetail from "./detail";
 
 const props = defineProps({
-    options: { type: Object, default: () => {} },
-    hidePageHeader: { type: Boolean, default: false },
-    hideHandler: { type: Boolean, default: false },
-    hideCheckbox: { type: Boolean, default: false }
+    selectable: { type: Boolean, default: false },
+    multiple: { type: Boolean, default: false },
+    paramsColums: { type: Array, default: () => [] },
+    options: { type: Object, default: () => ({}) }
 });
 
 const toolbarConfig = reactive({
@@ -54,13 +54,16 @@ const selectConfig = reactive({
 
 const formConfig = reactive({
     data: {},
-    items: [
+    items: computed(() => props.selectable ? [
+        mapFormItemInput("nameLike", "物料名称"),
+        mapFormItemInput("codeLike", "物料编码")
+    ]: [
         mapFormItemInput("nameLike", "物料名称"),
         mapFormItemInput("codeLike", "物料编码"),
         mapFormItemSelect("materialType", "物料类型", selectConfig),
         mapFormItemSelect("status", "物料状态", { ...selectConfig, options: objectToArray(statusDic) }),
         mapFormItemSelect("needType", "需求类型", { ...selectConfig, options: objectToArray(materialDic.needType) })
-    ]
+    ])
 });
 
 const paramsColums = reactive([
@@ -70,15 +73,24 @@ const paramsColums = reactive([
     { column: "materialType" },
     { column: "materialTypeIn" },
     { column: "status" },
-    { column: "needType" }
+    { column: "needType" },
+    ...props.paramsColums
 ]);
 
-const columns = reactive([
-    { visible: props.hideHandler, type: props.hideCheckbox && "radio" || "checkbox", fixed: "left", width: 40 },
-    { visible: !props.hideHandler, type: "seq", fixed: "left", width: 60 },
+const columns = computed(() => props.selectable ? [
+    { type: props.multiple && "checkbox" || "radio", fixed: "left", width: 40 },
+    { type: "html", field: "name", title: "物料名称", fixed: "left", minWidth: 150, sortable: true },
+    { type: "html", field: "code", title: "物料编码", fixed: "left", minWidth: 150, sortable: true },
+    { type: "html", field: "materialType", title: "物料类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(materialDic.type, cellValue, cellValue) },
+    { type: "html", field: "needType", title: "物料来源", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(materialDic.needType, cellValue, cellValue) },
+    { type: "html", field: "specification", title: "规格型号", minWidth: 120, sortable: true },
+    { type: "html", field: "unit", title: "单位", minWidth: 100, sortable: true },
+    { visible: false, type: "html", field: "price", title: "标准售价", minWidth: 100, sortable: true }
+]: [
+    { type: "seq", fixed: "left", width: 60 },
     { type: "html", field: "name", title: "物料名称", fixed: "left", minWidth: 150, sortable: true },
     { type: "html", field: "code", title: "物料编码", fixed: "left", minWidth: 150, sortable: true },
-    { visible: !props.hideHandler, field: "status", title: "物料状态", minWidth: 100, editRender: { name: "$cell-tag", options: statusDic } },
+    { field: "status", title: "物料状态", minWidth: 100, editRender: { name: "$cell-tag", options: statusDic } },
     { type: "html", field: "materialType", title: "物料类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(materialDic.type, cellValue, cellValue) },
     { type: "html", field: "needType", title: "物料来源", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(materialDic.needType, cellValue, cellValue) },
     { type: "html", field: "specification", title: "规格型号", minWidth: 120, sortable: true },
@@ -86,15 +98,12 @@ const columns = reactive([
     { visible: false, type: "html", field: "price", title: "标准售价", minWidth: 100, sortable: true },
     { type: "html", field: "createTime", title: "创建日期", minWidth: 120, sortable: true, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue, "YYYY-MM-DD") || cellValue },
     { visible: false, type: "html", field: "remark", title: "备注", minWidth: 300, sortable: true },
-    { visible: !props.hideHandler, title: "操作", fixed: "right", width: 220, slots: { default: "action" } }
+    { title: "操作", fixed: "right", width: 220, slots: { default: "action" } }
 ]);
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const materialRef = ref();
 const dialog = ref(false);

+ 1 - 4
src/views/basic/qualityPlan/index.vue

@@ -98,10 +98,7 @@ const columns = reactive([
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const planRef = ref();
 const planDescRef = ref();

+ 1 - 1
src/views/production/bom/detail.vue

@@ -21,7 +21,7 @@
                     <el-row>
                         <el-col :md="8" :xs="24">
                             <el-form-item label="产品编号" prop="material.code">
-                                <sc-table-input v-model="form.material" :hideShow="!!form.id" placeholder="选择产品" valueKey="code" tableKey="material" :options="selectOptions"></sc-table-input>
+                                <sc-table-input v-model="form.material" :hideShow="!!form.id" placeholder="选择产品" v-bind="selectOptions"></sc-table-input>
                             </el-form-item>
                         </el-col>
                         <el-col :md="8" :xs="24">

+ 9 - 16
src/views/production/bom/index.vue

@@ -2,7 +2,7 @@
 	<el-container class="is-vertical">
         <sc-page-header @add="table_add()"></sc-page-header>
         
-        <scTable ref="xGridTable" :apiObj="$API.production.bom" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="expandOptions">
+        <scTable ref="xGridTable" :apiObj="$API.production.bom" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" :treeConfig="treeConfig">
             <template #code_link="{ row }">
                 <vxe-text status="primary" @click="table_detail(row)">{{ row.bomCode }}</vxe-text>
             </template>
@@ -51,17 +51,13 @@ const toolbarConfig = reactive({
     export: true
 });
 
-const expandOptions = reactive({
-    options: {
-        treeConfig: {
-            lazy: true,
-            rowField: "id",
-            parentField: "parentId",
-            childrenField: "children",
-            hasChildField: "isHaveChildren",
-            loadMethod: ({ row }) => API.production.bom.getChild({ id: row.id })
-        }
-    }
+const treeConfig = reactive({
+    lazy: true,
+    rowField: "id",
+    parentField: "parentId",
+    childrenField: "children",
+    hasChildField: "isHaveChildren",
+    loadMethod: ({ row }) => API.production.bom.getChild({ id: row.id })
 })
 
 const selectConfig = reactive({
@@ -117,10 +113,7 @@ const columns = reactive([
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const bomRef = ref();
 const bomDescRef = ref();

+ 10 - 14
src/views/production/bom/main.js

@@ -21,24 +21,20 @@ export const tableOptions = reactive({
     mergeFooterItems: [{ row: 0, col: 0, rowspan: 1, colspan: 5 }],
 
     selectOptions: {
-        formConfig: {
-            data: { status: "enable", materialTypeIn: XEUtils.keys(XEUtils.omit(materialDic.type, "finished_product")) },
-            items: [
-                mapFormItemInput("nameLike", "物料名称"),
-                mapFormItemInput("codeLike", "物料编码")
-            ]
-        }
+        paramsColums: [
+            { column: "status", defaultValue: "enable" },
+            { column: "materialTypeIn", defaultValue: XEUtils.keys(XEUtils.omit(materialDic.type, "finished_product")) }
+        ]
     },
 
     add_success: (oldValue, newValue) => XEUtils.map(newValue, (item, index) => XEUtils.pick(item, "id", "code", "name", "specification", "unit"))
 })
 
 export const selectOptions = reactive({
-    formConfig: {
-        data: { status: "enable", materialTypeIn: materialDic.typeRelation["self_made"] },
-        items: [
-            mapFormItemInput("nameLike", "物料名称"),
-            mapFormItemInput("codeLike", "物料编码")
-        ]
-    }
+    tableKey: "material",
+    valueKey: "code",
+    paramsColums: [
+        { column: "status", defaultValue: "enable" },
+        { column: "materialTypeIn", defaultValue: materialDic.typeRelation["self_made"] }
+    ]
 })

+ 1 - 1
src/views/sales/order/detail.vue

@@ -16,7 +16,7 @@
                         </el-col>
                         <el-col :md="8" :xs="24">
                             <el-form-item label="客户" prop="customer.id">
-                                <sc-table-input v-model="form.customer" placeholder="选择客户" valueKey="name" tableKey="customer" :options="selectOptions"></sc-table-input>
+                                <sc-table-input v-model="form.customer" placeholder="选择客户" v-bind="selectOptions"></sc-table-input>
                             </el-form-item>
                         </el-col>
                         <el-col :md="8" :xs="24">

+ 2 - 5
src/views/sales/order/index.vue

@@ -87,15 +87,12 @@ const columns = reactive([
     { visible: false, type: "html", field: "createTime", title: "创建日期", minWidth: 120, sortable: true, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue, "YYYY-MM-DD") || cellValue },
     { visible: false, type: "html", field: "deliveryAddress", title: "客户收货地址", minWidth: 300, sortable: true },
     { visible: false, type: "html", field: "remark", title: "概要", minWidth: 300, sortable: true },
-    { title: "操作", fixed: "right", width: 320, slots: { default: "action" } }
+    { title: "操作", fixed: "right", width: 140, slots: { default: "action" } }
 ]);
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const orderRef = ref();
 const orderDescRef = ref();

+ 7 - 14
src/views/sales/order/main.js

@@ -21,24 +21,17 @@ export const tableOptions = reactive({
     mergeFooterItems: [{ row: 0, col: 0, rowspan: 1, colspan: 4 }],
 
     selectOptions: {
-        formConfig: {
-            data: { status: "enable", materialTypeIn: ["finished_product", "trade_goods"] },
-            items: [
-                mapFormItemInput("nameLike", "产品名称"),
-                mapFormItemInput("codeLike", "产品编码")
-            ]
-        }
+        paramsColums: [
+            { column: "status", defaultValue: "enable" },
+            { column: "materialTypeIn", defaultValue: materialDic.typeRelation["salable"] }
+        ]
     },
 
     add_success: (oldValue, newValue) => XEUtils.map(newValue, (item, index) => XEUtils.pick(item, "id", "code", "name", "unit", "price"))
 })
 
 export const selectOptions = reactive({
-    formConfig: {
-        data: { status: "enable" },
-        items: [
-            mapFormItemInput("nameLike", "客户名称"),
-            mapFormItemInput("codeLike", "客户编码")
-        ]
-    }
+    tableKey: "customer",
+    valueKey: "name",
+    paramsColums: [{ column: "status", defaultValue: "enable" }]
 })

+ 27 - 35
src/views/sales/plan/index.vue

@@ -2,7 +2,7 @@
 	<el-container class="is-vertical">
         <sc-page-header @add="table_add()"></sc-page-header>
         
-        <scTable ref="xGridTable" :apiObj="$API.sales.plan" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" :options="options">
+        <scTable ref="xGridTable" :apiObj="$API.sales.plan" :formConfig="formConfig" v-bind="options">
             <template #action="scoped">
                 <el-button v-if="XEUtils.includes(['year', 'quarter'], scoped.row.type)" type="primary" link @click="table_add(scoped.row)">
                     <template #icon><sc-iconify icon="ant-design:cloud-upload-outlined"></sc-iconify></template>新增
@@ -36,14 +36,6 @@ const formatStatus = row => {
     return "executing";
 }
 
-const toolbarConfig = reactive({
-    enabled: true,
-    export: true
-});
-
-const options = reactive({
-    treeConfig: { transform: true }
-})
 
 const selectConfig = reactive({
     options: objectToArray(salesDic.planType),
@@ -74,35 +66,35 @@ const formConfig = reactive({
     ]
 });
 
-const paramsColums = reactive([
-    { column: "orderBy", defaultValue: "code_asc" },
-    { column: "nameLike" },
-    { column: "codeLike" },
-    { column: "type" },
-    { column: "createTimeBegin", field: "createTime[0]" },
-    { column: "createTimeEnd", field: "createTime[1]" }
-]);
-
-const columns = reactive([
-    { type: "seq", fixed: "left", width: 80 },
-    { type: "html", field: "name", title: "计划名称", fixed: "left", minWidth: 160, treeNode: true, headerAlign: "center", align: "left", sortable: true },
-    { type: "html", field: "code", title: "计划编号", fixed: "left", minWidth: 150, sortable: true },
-    { type: "html", field: "type", title: "计划类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(salesDic.planType, cellValue, cellValue) },
-    { field: "status", title: "计划状态", minWidth: 120, editRender: { name: "$cell-tag", options: salesDic.planStatus, formatter: row => formatStatus(row) } },
-    { type: "html", field: "beginDate", title: "计划开始日期", minWidth: 150, sortable: true },
-    { type: "html", field: "endDate", title: "计划结束日期", minWidth: 150, sortable: true },
-    { type: "html", field: "saleAmount", title: "计划销售金额(万)", minWidth: 150, sortable: true },
-    { type: "html", field: "createTime", title: "创建日期", minWidth: 120, sortable: true, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue, "YYYY-MM-DD") || cellValue },
-    { visible: false, type: "html", field: "remark", title: "概要", minWidth: 300, sortable: true },
-    { title: "操作", fixed: "right", width: 220, slots: { default: "action" } }
-]);
+const options = reactive({
+    paramsColums: [
+        { column: "orderBy", defaultValue: "code_asc" },
+        { column: "nameLike" },
+        { column: "codeLike" },
+        { column: "type" },
+        { column: "createTimeBegin", field: "createTime[0]" },
+        { column: "createTimeEnd", field: "createTime[1]" }
+    ],
+    toolbarConfig: { enabled: true, export: true },
+    treeConfig: { transform: true },
+    columns: [
+        { type: "seq", fixed: "left", width: 80 },
+        { type: "html", field: "name", title: "计划名称", fixed: "left", minWidth: 160, treeNode: true, headerAlign: "center", align: "left", sortable: true },
+        { type: "html", field: "code", title: "计划编号", fixed: "left", minWidth: 150, sortable: true },
+        { type: "html", field: "type", title: "计划类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(salesDic.planType, cellValue, cellValue) },
+        { field: "status", title: "计划状态", minWidth: 120, editRender: { name: "$cell-tag", options: salesDic.planStatus, formatter: row => formatStatus(row) } },
+        { type: "html", field: "beginDate", title: "计划开始日期", minWidth: 150, sortable: true },
+        { type: "html", field: "endDate", title: "计划结束日期", minWidth: 150, sortable: true },
+        { type: "html", field: "saleAmount", title: "计划销售金额(万)", minWidth: 150, sortable: true },
+        { type: "html", field: "createTime", title: "创建日期", minWidth: 120, sortable: true, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue, "YYYY-MM-DD") || cellValue },
+        { visible: false, type: "html", field: "remark", title: "概要", minWidth: 300, sortable: true },
+        { title: "操作", fixed: "right", width: 220, slots: { default: "action" } }
+    ]
+});
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const planRef = ref();
 const dialog = ref(false);

+ 23 - 47
src/views/system/dept/index.vue

@@ -2,7 +2,7 @@
 	<el-container class="is-vertical">
         <sc-page-header @add="table_add"></sc-page-header>
 
-        <scTable ref="xGridTable" :apiObj="$API.system.dept" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" :options="options">
+        <scTable ref="xGridTable" :apiObj="$API.system.dept" v-bind="options">
             <template #action="{ row }">
                 <template v-if="row.pid != 0">
                     <el-button type="primary" link @click="table_edit(row)">
@@ -16,7 +16,7 @@
         </scTable>
 	</el-container>
 
-    <dept-detail v-if="dialog.detail" ref="deptRef" @success="refreshTable" @closed="dialog.detail = false"></dept-detail>
+    <dept-detail v-if="dialog" ref="deptRef" @success="refreshTable" @closed="dialog = false"></dept-detail>
 </template>
 
 <script setup>
@@ -27,65 +27,41 @@ import { mapFormItemInput } from "@/components/scTable/helper";
 import deptDetail from "./detail";
 
 const options = reactive({
-    treeConfig: {
-        transform: true, // 自动将列表转为树结构
-        rowField: "id",
-        parentField: "pid"
-    },
-    checkboxConfig: {
-        highlight: true,
-        range: false, // 鼠标在复选框的列内滑动选中或取消指定行
-        isShiftKey: false // 鼠标点击和 shift 键选取指定范围的行
+    formConfig: {
+        data: {},
+        items: [mapFormItemInput("nameLike", "部门名称")]
     },
+    paramsColums: [
+        { column: "orderBy", defaultValue: "deptSort_asc" },
+        { column: "nameLike" }
+    ],
+    toolbarConfig: { enabled: true, export: true },
+    treeConfig: { transform: true, parentField: "pid" },
+    columns: [
+        { type: "seq", width: 60 },
+        { type: "html", field: "name", title: "部门名称", minWidth: 200, treeNode: true, sortable: true },
+        { type: "html", field: "firmFunctionary", title: "负责人", minWidth: 160, sortable: true },
+        { type: "html", field: "firmFunctionaryPhone", title: "负责人电话", minWidth: 160, sortable: true },
+        { type: "html", field: "remark", title: "备注", minWidth: 300, sortable: true },
+        { title: "操作", fixed: "right", width: 140, align: "center", slots: { default: "action" } }
+    ],
     pagerConfig: { enabled: false }
 });
 
-
-const toolbarConfig = reactive({
-    enabled: true,
-    export: true
-});
-
-const formConfig = reactive({
-    data: {},
-    items: [
-        mapFormItemInput("nameLike", "部门名称")
-    ]
-});
-
-const paramsColums = reactive([
-    { column: "orderBy", defaultValue: "deptSort_asc" },
-    { column: "nameLike" }
-]);
-
-const columns = reactive([
-    { type: "seq", width: 60 },
-    { type: "html", field: "name", title: "部门名称", minWidth: 200, treeNode: true, sortable: true },
-    { type: "html", field: "firmFunctionary", title: "负责人", minWidth: 160, sortable: true },
-    { type: "html", field: "firmFunctionaryPhone", title: "负责人电话", minWidth: 160, sortable: true },
-    { type: "html", field: "remark", title: "备注", minWidth: 300, sortable: true },
-    { title: "操作", fixed: "right", width: 140, align: "center", slots: { default: "action" } }
-]);
-
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const deptRef = ref();
-const dialog = reactive({
-    detail: false
-});
+const dialog = ref(false);
 
 const table_add = () => {
-    dialog.detail = true;
+    dialog.value = true;
     nextTick(() => deptRef.value?.open());
 }
 
 const table_edit = row => {
-    dialog.detail = true;
+    dialog.value = true;
     nextTick(() => deptRef.value?.setData(row));
 }
 

+ 3 - 5
src/views/system/role/index.vue

@@ -56,14 +56,12 @@ const columns = reactive([
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const roleRef = ref();
 const dialog = reactive({
-    detail: false
+    detail: false,
+    bind: false
 });
 
 const table_add = () => {

+ 5 - 10
src/views/system/user/index.vue

@@ -38,7 +38,7 @@
         </el-container>
 	</el-container>
 
-    <user-detail v-if="dialog.detail" ref="userRef" :deptTree="deptTree" @success="refreshTable" @closed="dialog.detail = false"></user-detail>
+    <user-detail v-if="dialog" ref="userRef" :deptTree="deptTree" @success="refreshTable" @closed="dialog = false"></user-detail>
 </template>
 
 <script setup>
@@ -105,23 +105,18 @@ const columns = reactive([
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const userRef = ref();
-const dialog = reactive({
-    detail: false
-});
+const dialog = ref(false);
 
 const table_add = () => {
-    dialog.detail = true;
+    dialog.value = true;
     nextTick(() => userRef.value?.open());
 }
 
 const table_edit = row => {
-    dialog.detail = true;
+    dialog.value = true;
     nextTick(() => userRef.value?.setData(row));
 }
 

+ 3 - 10
src/views/userCenter/index.vue

@@ -62,7 +62,7 @@ import XEUtils from "xe-utils";
 
 import API from "@/api";
 import TOOL from "@/utils/tool";
-const store = useStore()
+const store = useStore();
 
 const isSaving = ref(false);
 const form = ref(XEUtils.clone(TOOL.data.get("USER_INFO"), true));
@@ -117,14 +117,7 @@ const submit = () => {
 }
 
 @media (max-width: 992px) {
-    // .user-container {
-    //     flex-direction: column;
-    //     .el-card:first-child {
-    //         width: 100%;margin-bottom: 15px;margin-right: 0;
-    //         .el-form .el-form-item :deep(.el-form-item__label) {
-    //             justify-content: flex-start;
-    //         }
-    //     }
-    // }
+    .user-container .el-card {width: 100%;}
+    .el-card + .el-card {margin-top: 15px;margin-left: 0;}
 }
 </style>

+ 1 - 1
src/views/workmanship/line/desc.vue

@@ -19,7 +19,7 @@
                 </el-collapse-item>
 
                 <el-collapse-item title="加工路线" name="route">
-                    <sc-form-table v-model="descData.detailList" v-bind="options" ></sc-form-table>
+                    <sc-form-table v-model="descData.detailList" v-bind="options"></sc-form-table>
                 </el-collapse-item>
 
                 <el-collapse-item title="质检方案" name="plan">

+ 5 - 35
src/views/workmanship/line/history.vue

@@ -1,53 +1,23 @@
 <template>
     <el-dialog v-model="visible" title="历史版本" width="80%" :close-on-click-modal="false" @closed="$emit('closed')">
-        <data-table ref="tableRef" hidePageHeader hideHandler :options="tableOptions"></data-table>
-
-        <template #footer>
-            <el-button auto-insert-space @click="visible = false">取消</el-button>
-            <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">选定并结束</el-button>
-        </template>
+        <data-table hidePageHeader hideHandler :paramsColums="paramsColums" :options="tableOptions"></data-table>
     </el-dialog>
 </template>
 
 <script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import { mapFormItemInput, mapFormItemDatePicker } from "@/components/scTable/helper";
 import dataTable from "@/views/workmanship/line/index";
 
 const $emit = defineEmits(["closed"]);
-const visible = ref(false);
-
-const daterangeConfig = reactive({
-    span: 7,
-    resetValue: () => [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().endOf("day").format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "daterange",
-        startPlaceholder: "开始日期",
-        endPlaceholder: "结束日期",
-        format: "YYYY-MM-DD"
-    }
-});
-
-const tableRef = ref();
+const paramsColums = reactive([]);
 const tableOptions = reactive({
     maxHeight: 1048,
-    toolbarConfig: { enabled: true, print: false, zoom: false },
-    formConfig: {
-        data: {
-            createTime: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().endOf("day").format("YYYY-MM-DD HH:mm:ss")]
-        },
-        items: [
-            mapFormItemInput("nameLike", "工艺路线名称"),
-            mapFormItemInput("codeLike", "工艺路线编号"),
-            mapFormItemDatePicker("createTime", "创建日期", daterangeConfig)
-        ]
-    }
+    toolbarConfig: { enabled: true, print: false, zoom: false, custom: false }
 });
 
+const visible = ref(false);
 const setData = data => {
     visible.value = true;
-    XEUtils.set(tableOptions.formConfig.data, "parentId", data.id);
+    paramsColums.push({ column: "parentId", defaultValue: data.id });
 }
 
 defineExpose({

+ 15 - 9
src/views/workmanship/line/index.vue

@@ -1,8 +1,8 @@
 <template>
 	<el-container class="is-vertical">
-        <sc-page-header @add="table_add"></sc-page-header>
+        <sc-page-header v-if="!hidePageHeader" @add="table_add"></sc-page-header>
 
-        <scTable ref="xGridTable" :apiObj="$API.workmanship.route" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns">
+        <scTable ref="xGridTable" :apiObj="$API.workmanship.route" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="options">
             <template #code_link="{ row }">
                 <vxe-text status="primary" @click="table_detail(row)">{{ row.code }}</vxe-text>
             </template>
@@ -52,12 +52,20 @@ import processDetail from "./detail";
 import processDesc from "./desc";
 import versionHistory from "./history";
 
+const props = defineProps({
+    hidePageHeader: { type: Boolean, default: false },
+    hideHandler: { type: Boolean, default: false },
+    paramsColums: { type: Array, default: () => [] },
+    options: { type: Object, default: () => ({}) }
+});
+
 const toolbarConfig = reactive({
     enabled: true,
     export: true
 });
 
 const selectConfig = reactive({
+    visible: !props.hideHandler,
     options: objectToArray(statusDic),
     events: {
         change: data => XEUtils.merge(formConfig.data, data)
@@ -91,28 +99,26 @@ const paramsColums = reactive([
     { column: "codeLike" },
     { column: "status" },
     { column: "createTimeBegin", field: "createTime[0]" },
-    { column: "createTimeEnd", field: "createTime[1]" }
+    { column: "createTimeEnd", field: "createTime[1]" },
+    ...props.paramsColums
 ]);
 
 const columns = reactive([
     { type: "seq", fixed: "left", width: 60 },
     { type: "html", field: "name", title: "工艺路线名称", fixed: "left", minWidth: 150, sortable: true },
     { field: "code", title: "工艺路线编号", fixed: "left", minWidth: 150, sortable: true, className: "vxe-table-link-cell", slots: { default: "code_link" } },
-    { field: "status", title: "工艺路线状态", minWidth: 120, editRender: { name: "$cell-tag", options: statusDic } },
+    { visible: !props.hideHandler, field: "status", title: "工艺路线状态", minWidth: 120, editRender: { name: "$cell-tag", options: statusDic } },
     { visible: false, type: "html", field: "timeUnit", title: "时间单位", minWidth: 100, sortable: true, formatter: ({ cellValue }) => XEUtils.get(workmanshipDic.timeUnit, cellValue, cellValue) },
     { type: "html", field: "", title: "质检方案", minWidth: 160, sortable: true },
     { type: "html", field: "createTime", title: "创建日期", minWidth: 120, sortable: true, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue, "YYYY-MM-DD") || cellValue },
     { type: "html", field: "version", title: "版本号", minWidth: 120, sortable: true, className: "vxe-table-link-cell", slots: { default: "version_link" } },
     { visible: false, type: "html", field: "remark", title: "概要", minWidth: 300, sortable: true },
-    { title: "操作", fixed: "right", width: 320, slots: { default: "action" } }
+    { visible: !props.hideHandler, title: "操作", fixed: "right", width: 320, slots: { default: "action" } }
 ]);
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const processRef = ref();
 const processDescRef = ref();

+ 1 - 7
src/views/workmanship/line/main.js

@@ -38,13 +38,7 @@ export const tableOptions = reactive({
     mergeFooterItems: [{ row: 0, col: 0, rowspan: 1, colspan: 5 }],
 
     selectOptions: {
-        formConfig: {
-            data: { status: "enable" },
-            items: [
-                mapFormItemInput("nameLike", "工序名称"),
-                mapFormItemInput("codeLike", "工序编号")
-            ]
-        }
+        paramsColums: [{ column: "status", defaultValue: "enable" }]
     },
 
     add_success: (oldValue, newValue) => {

+ 24 - 17
src/views/workmanship/process/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<el-container class="is-vertical">
-        <sc-page-header v-if="!hidePageHeader" @add="table_add"></sc-page-header>
+        <sc-page-header v-if="!selectable" @add="table_add"></sc-page-header>
 
         <scTable ref="xGridTable" :apiObj="$API.workmanship.process" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="options">
             <template #action="{ row }">
@@ -34,10 +34,10 @@ import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/co
 import processDetail from "./detail";
 
 const props = defineProps({
-    options: { type: Object, default: () => {} },
-    hidePageHeader: { type: Boolean, default: false },
-    hideHandler: { type: Boolean, default: false },
-    hideCheckbox: { type: Boolean, default: false }
+    selectable: { type: Boolean, default: false },
+    multiple: { type: Boolean, default: false },
+    paramsColums: { type: Array, default: () => [] },
+    options: { type: Object, default: () => ({}) }
 });
 
 const toolbarConfig = reactive({
@@ -64,13 +64,16 @@ const daterangeConfig = reactive({
 
 const formConfig = reactive({
     data: {},
-    items: [
+    items: computed(() => props.selectable ? [
+        mapFormItemInput("nameLike", "工序名称"),
+        mapFormItemInput("codeLike", "工序编号")
+    ]: [
         mapFormItemInput("nameLike", "工序名称"),
         mapFormItemInput("codeLike", "工序编号"),
         mapFormItemSelect("status", "工序状态", selectConfig),
         mapFormItemSelect("category", "工序分类", { ...selectConfig, options: objectToArray(workmanshipDic.category) }),
         mapFormItemDatePicker("createTime", "创建日期", daterangeConfig)
-    ]
+    ])
 });
 
 const paramsColums = reactive([
@@ -80,15 +83,22 @@ const paramsColums = reactive([
     { column: "status" },
     { column: "category" },
     { column: "createTimeBegin", field: "createTime[0]" },
-    { column: "createTimeEnd", field: "createTime[1]" }
+    { column: "createTimeEnd", field: "createTime[1]" },
+    ...props.paramsColums
 ]);
 
-const columns = reactive([
-    { visible: props.hideHandler, type: props.hideCheckbox && "radio" || "checkbox", fixed: "left", width: 40 },
-    { visible: !props.hideHandler, type: "seq", fixed: "left", width: 60 },
+const columns = computed(() => props.selectable ? [
+    { type: props.multiple && "checkbox" || "radio", fixed: "left", width: 40 },
     { type: "html", field: "name", title: "工序名称", fixed: "left", minWidth: 150, sortable: true },
     { type: "html", field: "code", title: "工序编号", fixed: "left", minWidth: 150, sortable: true },
-    { visible: !props.hideHandler, field: "status", title: "工序状态", minWidth: 100, editRender: { name: "$cell-tag", options: statusDic } },
+    { type: "html", field: "category", title: "工序分类", minWidth: 100, sortable: true, formatter: ({ cellValue }) => XEUtils.get(workmanshipDic.category, cellValue, cellValue) },
+    { type: "html", field: "processType", title: "默认加工类型", minWidth: 140, sortable: true, formatter: ({ cellValue }) => XEUtils.get(workmanshipDic.type, cellValue, cellValue) },
+    { type: "html", field: "calculateMethod", title: "工资默认计算方式", minWidth: 140, sortable: true, formatter: ({ cellValue }) => XEUtils.get(workmanshipDic.calcMethod, cellValue, cellValue) }
+] : [
+    { type: "seq", fixed: "left", width: 60 },
+    { type: "html", field: "name", title: "工序名称", fixed: "left", minWidth: 150, sortable: true },
+    { type: "html", field: "code", title: "工序编号", fixed: "left", minWidth: 150, sortable: true },
+    { field: "status", title: "工序状态", minWidth: 100, editRender: { name: "$cell-tag", options: statusDic } },
     { type: "html", field: "category", title: "工序分类", minWidth: 100, sortable: true, formatter: ({ cellValue }) => XEUtils.get(workmanshipDic.category, cellValue, cellValue) },
     { type: "html", field: "directorName", title: "工序负责人", minWidth: 120, sortable: true },
     { visible: false, type: "html", field: "directorPhone", title: "联系方式", minWidth: 120, sortable: true },
@@ -96,15 +106,12 @@ const columns = reactive([
     { visible: false, type: "html", field: "calculateMethod", title: "工资默认计算方式", minWidth: 140, sortable: true, formatter: ({ cellValue }) => XEUtils.get(workmanshipDic.calcMethod, cellValue, cellValue) },
     { type: "html", field: "createTime", title: "创建日期", minWidth: 120, sortable: true, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue, "YYYY-MM-DD") || cellValue },
     { visible: false, type: "html", field: "remark", title: "概要", minWidth: 300, sortable: true },
-    { visible: !props.hideHandler, title: "操作", fixed: "right", width: 220, slots: { default: "action" } }
+    { title: "操作", fixed: "right", width: 220, slots: { default: "action" } }
 ]);
 
 // 显示隐藏 筛选表单
 const xGridTable = ref();
-const refreshTable = (mode = "add") => {
-    xGridTable.value.reloadColumn(columns);
-    xGridTable.value.searchData(mode);
-}
+const refreshTable = (mode = "add") => xGridTable.value.searchData(mode);
 
 const processRef = ref();
 const dialog = ref(false);