Z пре 2 година
комит
564dcedd84

+ 142 - 0
src/api/bpm/visPage.js

@@ -0,0 +1,142 @@
+import request from '@/utils/request'
+//获取签单金额
+export async function getSignAmountAPI(query) {
+  const res = await request({
+    url: '/dataScreen/getSignAmount',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//获取交货金额
+export async function getDeliveryAmountAPI(query) {
+  const res = await request({
+    url: '/dataScreen/getDeliveryAmount',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//获取销售金额
+export async function getSalesAmountAPI(query) {
+  const res = await request({
+    url: '/dataScreen/getSalesAmount',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//获取增重重量
+export async function getIncreaseWeightAPI(query) {
+  const res = await request({
+    url: '/dataScreen/getIncreaseWeight',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//获取成品入库重量
+export async function getProductInStoreWeightAPI(query) {
+  const res = await request({
+    url: '/dataScreen/getProductInStoreWeight',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//获取月度销量统计
+export async function getMonthlySalesStatisticAPI(query) {
+  const res = await request({
+    url: '/dataScreen/getMonthlySalesStatistic',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//获取月度产量统计
+export async function getMonthlyProduceStatisticAPI(query) {
+  const res = await request({
+    url: '/dataScreen/getMonthlyProduceStatistic',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//获取月度产量统计
+export async function getSalesFinishListAPI(query) {
+  const res = await request({
+    url: '/dataScreen/getSalesFinishList',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//月度采购总金额(万元)
+export async function getMonthlyPurchaseAmountAPI() {
+  const res = await request({
+    url: '/dataScreen/getMonthlyPurchaseAmount',
+    method: 'get'
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//年度采购总金额(万元)
+export async function getYearlyPurchaseAmountAPI() {
+  const res = await request({
+    url: '/dataScreen/getYearlyPurchaseAmount',
+    method: 'get'
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//月度采购明细 列表(万元)
+export async function getMonthlyPurchaseDetailAPI() {
+  const res = await request({
+    url: '/dataScreen/getMonthlyPurchaseDetail',
+    method: 'get'
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//年度采购统计 (万元)
+export async function getYearlyPurchaseStatisticAPI() {
+  const res = await request({
+    url: '/dataScreen/getYearlyPurchaseStatistic',
+    method: 'get'
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

BIN
src/assets/TGLOGO.png


BIN
src/assets/border1.png


BIN
src/assets/border2.png


BIN
src/assets/border3.png


BIN
src/assets/border4.png


BIN
src/assets/fonts/AlibabaPuHuiTi-2-45-Light.ttf


BIN
src/assets/fonts/DS-DIGIT.TTF


BIN
src/assets/fonts/LCD2B___.TTF


BIN
src/assets/fonts/优设标题黑.ttf


BIN
src/assets/png/border7.png


BIN
src/assets/png/border8.png


+ 48 - 0
src/assets/svg二/border5.svg

@@ -0,0 +1,48 @@
+<svg width="1910" height="421" viewBox="0 0 1910 421" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g filter="url(#filter0_f_3260_2402)">
+<mask id="path-1-inside-1_3260_2402" fill="white">
+<path d="M55.0815 5L5.08154 5L5.08154 55L55.0815 55L55.0815 5Z"/>
+</mask>
+<path d="M5.08154 5H3.55947V3.47793H5.08154V5ZM55.0815 6.52207L5.08154 6.52207V3.47793L55.0815 3.47793V6.52207ZM6.60361 5L6.60361 55H3.55947L3.55947 5H6.60361Z" fill="#B5CEF4" mask="url(#path-1-inside-1_3260_2402)"/>
+</g>
+<g filter="url(#filter1_f_3260_2402)">
+<mask id="path-3-inside-2_3260_2402" fill="white">
+<path d="M1905.08 55V5H1859.08V55H1905.08Z"/>
+</mask>
+<path d="M1905.08 5V3.47793H1906.6V5H1905.08ZM1903.56 55V5H1906.6V55H1903.56ZM1905.08 6.52207H1859.08V3.47793H1905.08V6.52207Z" fill="#B5CEF4" mask="url(#path-3-inside-2_3260_2402)"/>
+</g>
+<g filter="url(#filter2_f_3260_2402)">
+<mask id="path-5-inside-3_3260_2402" fill="white">
+<path d="M1905.08 366.002L1905.08 416.002L1859.08 416.002L1859.08 366.002L1905.08 366.002Z"/>
+</mask>
+<path d="M1905.08 416.002L1905.08 417.524L1906.6 417.524L1906.6 416.002L1905.08 416.002ZM1903.56 366.002L1903.56 416.002L1906.6 416.002L1906.6 366.002L1903.56 366.002ZM1905.08 414.48L1859.08 414.48L1859.08 417.524L1905.08 417.524L1905.08 414.48Z" fill="#B5CEF4" mask="url(#path-5-inside-3_3260_2402)"/>
+</g>
+<g filter="url(#filter3_f_3260_2402)">
+<mask id="path-7-inside-4_3260_2402" fill="white">
+<path d="M5 366.002L5 416.002H55V366.002H5Z"/>
+</mask>
+<path d="M5 416.002L5 417.524H3.47793V416.002H5ZM6.52207 366.002L6.52207 416.002H3.47793L3.47793 366.002H6.52207ZM5 414.48H55V417.524H5L5 414.48Z" fill="#B5CEF4" mask="url(#path-7-inside-4_3260_2402)"/>
+</g>
+<defs>
+<filter id="filter0_f_3260_2402" x="0.971954" y="0.890411" width="58.2192" height="58.2192" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="2.05479" result="effect1_foregroundBlur_3260_2402"/>
+</filter>
+<filter id="filter1_f_3260_2402" x="1854.97" y="0.890411" width="54.2192" height="58.2192" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="2.05479" result="effect1_foregroundBlur_3260_2402"/>
+</filter>
+<filter id="filter2_f_3260_2402" x="1854.97" y="361.892" width="54.2192" height="58.2192" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="2.05479" result="effect1_foregroundBlur_3260_2402"/>
+</filter>
+<filter id="filter3_f_3260_2402" x="0.890411" y="361.892" width="58.2192" height="58.2192" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="2.05479" result="effect1_foregroundBlur_3260_2402"/>
+</filter>
+</defs>
+</svg>

+ 31 - 0
src/assets/svg二/border6.svg

@@ -0,0 +1,31 @@
+<svg width="792" height="123" viewBox="0 0 792 123" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M742.834 0.702293C769.754 0.702293 791.501 20.6697 791.501 45.2101V77.6404C791.501 102.181 769.754 122.148 742.834 122.148H208.447H49.1662C22.2458 122.148 0.499168 102.181 0.499168 77.6404V45.2101C0.499168 20.6697 22.2458 0.702293 49.1662 0.702293H742.834Z" fill="url(#paint0_linear_3260_2239)" stroke="#2A4369" stroke-width="0.998336"/>
+<mask id="mask0_3260_2239" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="792" height="123">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M737.786 0C767.728 0 792 20.2417 792 45.2112V77.7888C792 102.758 767.728 123 737.786 123H229.846H54.2136C24.2724 123 0 102.758 0 77.7888V45.2112C0 20.2417 24.2724 0 54.2136 0H737.786Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_3260_2239)">
+<g opacity="0.637642" filter="url(#filter0_f_3260_2239)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M163.862 103.723C216.008 103.723 258.281 121.415 258.281 143.239C258.281 165.063 216.008 182.755 163.862 182.755C111.716 182.755 69.4434 165.063 69.4434 143.239C69.4434 121.415 111.716 103.723 163.862 103.723V103.723Z" fill="#536C92"/>
+</g>
+</g>
+<path d="M42.6411 118.195H285.084" stroke="url(#paint1_linear_3260_2239)" stroke-width="2.97046"/>
+<defs>
+<filter id="filter0_f_3260_2239" x="18.3046" y="52.5839" width="291.115" height="181.309" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="25.5694" result="effect1_foregroundBlur_3260_2239"/>
+</filter>
+<linearGradient id="paint0_linear_3260_2239" x1="0" y1="0.203125" x2="0" y2="122.647" gradientUnits="userSpaceOnUse">
+<stop stop-color="#21395F"/>
+<stop offset="0.251181" stop-color="#002C41" stop-opacity="0.501961"/>
+<stop offset="0.499804" stop-color="#000A25" stop-opacity="0.01"/>
+<stop offset="0.734139" stop-color="#002139" stop-opacity="0.501961"/>
+<stop offset="1" stop-color="#2D466C"/>
+</linearGradient>
+<linearGradient id="paint1_linear_3260_2239" x1="-nan" y1="-nan" x2="-nan" y2="-nan" gradientUnits="userSpaceOnUse">
+<stop stop-color="#C8C8C8" stop-opacity="0.01"/>
+<stop offset="0.512724" stop-color="#00FFF4"/>
+<stop offset="1" stop-color="#979797" stop-opacity="0.01"/>
+</linearGradient>
+</defs>
+</svg>

+ 15 - 0
src/assets/svg二/border7.svg

@@ -0,0 +1,15 @@
+<svg width="1742" height="65" viewBox="0 0 1742 65" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M34.6213 1.00829L1740.78 1C1740.95 1 1741.05 1.19892 1740.97 1.36469L1711 62.3672C1710.51 63.3728 1709.56 63.9917 1708.53 63.9917L5.16036 64C1.96568 64 -0.0409711 60.1652 1.56635 57.1041L29.0306 4.57782C30.1886 2.36758 32.3151 1.00829 34.6213 1.00829Z" stroke="#314A70" stroke-miterlimit="10"/>
+<path d="M39.1802 3.5H1737.08C1737.25 3.5 1737.35 3.68 1737.27 3.83L1707.25 59.03C1706.76 59.94 1705.81 60.5 1704.77 60.5H9.67022C6.47022 60.5 4.46022 57.03 6.07022 54.26L33.5802 6.73C34.7402 4.73 36.8702 3.5 39.1802 3.5Z" fill="url(#paint0_linear_3260_2390)" stroke="url(#paint1_linear_3260_2390)" stroke-miterlimit="10"/>
+<defs>
+<linearGradient id="paint0_linear_3260_2390" x1="5.50293" y1="32" x2="717.299" y2="32" gradientUnits="userSpaceOnUse">
+<stop stop-color="#314A70"/>
+<stop offset="1" stop-color="#324B71"/>
+</linearGradient>
+<linearGradient id="paint1_linear_3260_2390" x1="5.50293" y1="32" x2="717.299" y2="32" gradientUnits="userSpaceOnUse">
+<stop stop-color="white" stop-opacity="0.47"/>
+<stop offset="0.51" stop-color="#00F0FF"/>
+<stop offset="1" stop-color="white" stop-opacity="0"/>
+</linearGradient>
+</defs>
+</svg>

+ 22 - 0
src/assets/svg二/border8.svg

@@ -0,0 +1,22 @@
+<svg width="56" height="68" viewBox="0 0 56 68" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0.418742 0.418742H55.1217V67.0762H0.418742V0.418742Z" fill="url(#paint0_linear_3260_2253)" stroke="#0066FF" stroke-width="0.837483"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.76319 0V6.1359H0V0H1.76319V0Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.41523 0V2.1427H0V0H8.41523V0Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.76319 9.81836V58.9056H0V9.81836H1.76319V9.81836Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M55.6206 9.81836V58.9056H53.8574V9.81836H55.6206V9.81836Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M53.8574 0V6.1359H55.6206V0L53.8574 0V0Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M47.1248 0V2.1427L55.54 2.1427V0L47.1248 0V0Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.68305 66.4238L1.68305 60.2879H0L0 66.4238H1.68305V66.4238Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.41523 67.3398V65.1971H0L0 67.3398H8.41523V67.3398Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M43.7591 67.3398V65.1971H11.7812V67.3398H43.7591V67.3398Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M43.7591 2.14258V-0.000118256L11.7812 -0.000118256V2.14258L43.7591 2.14258V2.14258Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M53.8575 66.4238V60.2879H55.5405V66.4238H53.8575V66.4238Z" fill="#5B749A"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M47.1248 67.3398V65.1971H55.54V67.3398H47.1248V67.3398Z" fill="#5B749A"/>
+<defs>
+<linearGradient id="paint0_linear_3260_2253" x1="0.00027808" y1="0" x2="0.00027808" y2="67.4949" gradientUnits="userSpaceOnUse">
+<stop stop-color="#415A80"/>
+<stop offset="0.499804" stop-color="#6E87AD"/>
+<stop offset="1" stop-color="#30496F"/>
+</linearGradient>
+</defs>
+</svg>

+ 22 - 0
src/store/getters.js

@@ -0,0 +1,22 @@
+/**
+ * vuex getter
+ */
+import dictEnum from '@/enum/dict';
+export default {
+  user: (state) => state.user,
+  theme: (state) => state.theme,
+  dict: (state) => state.dict,
+  // 根据字典enumName  和 dictCode 获取字典项
+  getDict: (state) => (enumName, dictCode) =>
+    (state.dict[dictEnum[enumName]] || []).find(
+      (item) => item.dictCode === dictCode
+    ) || {},
+  // 根据字典enumName  和 dictCode 获取字典 值(名称
+  getDictValue: (state) => (enumName, dictCode) => {
+    const obj = (state.dict[dictEnum[enumName]] || []).find((item) => {
+      return item.dictCode === dictCode;
+    });
+
+    return obj && obj.dictValue;
+  }
+};

+ 38 - 0
src/store/index.js

@@ -0,0 +1,38 @@
+/**
+ * vuex状态管理
+ */
+import Vue from 'vue';
+import Vuex from 'vuex';
+import getters from './getters';
+import dict from './modules/dict';
+import user from './modules/user';
+import selectCache from './modules/selectCache';
+import theme from './modules/theme';
+import createPersistedState from 'vuex-persistedstate';
+
+import { SYSTEM_NAME } from '@/config/setting';
+
+Vue.use(Vuex);
+
+export default new Vuex.Store({
+  state: {},
+  mutations: {},
+  actions: {},
+  modules: {
+    dict,
+    user,
+    selectCache,
+    theme
+  },
+  getters,
+  plugins: [
+    //会自动保存创建的状态。刷新还在
+    createPersistedState({
+      key: window.__POWERED_BY_QIANKUN__
+        ? `vuex-${SYSTEM_NAME}`
+        : `private-vuex-${SYSTEM_NAME}`,
+      storage: window.sessionStorage,
+      paths: ['user']
+    })
+  ]
+});

+ 98 - 0
src/store/modules/dict.js

@@ -0,0 +1,98 @@
+import dictEnum, { numberList } from '@/enum/dict';
+import Vue from 'vue';
+import { getByCode } from '@/api/system/dictionary-data';
+
+//非枚举定义
+// const otherDictConfig = {
+//   //   [dictEnum.物品类型]: {
+//   //     request: getSubListByParentId,
+//   //     dictCode: 'type',
+//   //     dictValue: 'name',
+//   //     resKey: '' //为空选 data
+//   //   }
+// };
+
+const state = {};
+
+const mutations = {
+  //根据字典code 添加字典
+  ADD_DICT: (state, { code, dict }) => {
+    Vue.set(state, code, dict);
+  }
+};
+
+const actions = {
+  // 根据字典enumName请求字典 已获取的不做重复请求
+  async requestDict({ commit, state }, enumName) {
+    const code = dictEnum[enumName];
+
+    if (state[code]?.length) return state[code];
+    let res;
+    // if (otherDictConfig[dictEnum[enumName]]) {
+    //   const config = otherDictConfig[dictEnum[enumName]];
+    //   console.log(config);
+    //   //非枚举定义
+    //   res = await config.request();
+    //   if (res?.code == 0) {
+    //     let list = config.resKey ? res.data[config.resKey] : res.data;
+    //     commit('ADD_DICT', {
+    //       code,
+    //       dict: list.map((item) => ({
+    //         ...item,
+    //         dictCode: item[config.dictCode],
+    //         dictValue: item[config.dictValue]
+    //       }))
+    //     });
+    //     return res.list;
+    //   }
+    // } else {
+    res = await getByCode(code);
+    if (res?.code == 0) {
+      const isNumber = numberList.includes(code);
+      commit('ADD_DICT', {
+        code,
+        dict: res.data.map((item) => {
+          const arr = Object.entries(item)[0] || [];
+
+          return {
+            dictCode: isNumber ? Number(arr[0]) : arr[0],
+            dictValue: arr[1]
+          };
+        })
+      });
+      return res.data;
+    }
+    // }
+
+    return [];
+  },
+  // 更新字典
+  async reloadRequestDict({ commit }, enumName) {
+    const code = dictEnum[enumName];
+    const res = await getByCode(code);
+    const isNumber = numberList.includes(code);
+    if (res?.code == 0) {
+      commit('ADD_DICT', {
+        code,
+        dict: res.data.map((item) => {
+          const arr = Object.entries(item);
+
+          return {
+            dictCode: isNumber ? Number(arr[0]) : arr[0],
+            dictValue: arr[1]
+          };
+        })
+      });
+      return res.data;
+    }
+
+    return [];
+  }
+};
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions
+};

+ 18 - 0
src/store/modules/selectCache.js

@@ -0,0 +1,18 @@
+// 公共选择组件数据缓存
+import Vue from 'vue';
+const state = {};
+
+const mutations = {
+  ADD_SELECT: (state, { name, dict }) => {
+    Vue.set(state, name, dict);
+  }
+};
+
+const actions = {};
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions
+};

+ 637 - 0
src/store/modules/theme.js

@@ -0,0 +1,637 @@
+import Vue from 'vue';
+/**
+ * 主题状态管理
+ */
+import {
+  changeColor,
+  screenWidth,
+  screenHeight,
+  contentWidth,
+  contentHeight,
+  WEAK_CLASS,
+  BODY_LIMIT_CLASS,
+  DISABLES_CLASS
+} from 'ele-admin';
+import {
+  TAB_KEEP_ALIVE,
+  KEEP_ALIVE_EXCLUDES,
+  THEME_STORE_NAME
+} from '@/config/setting';
+
+/**
+ * state 默认值
+ */
+const DEFAULT_STATE = Object.freeze({
+  // 页签数据
+  tabs: [],
+  // 是否折叠侧栏
+  collapse: false,
+  // 是否折叠一级侧栏
+  sideNavCollapse: false,
+  // 内容区域是否全屏
+  bodyFullscreen: false,
+  // 是否开启页签栏
+  showTabs: true,
+  // 是否开启页脚
+  showFooter: true,
+  // 顶栏风格: light(亮色), dark(暗色), primary(主色)
+  headStyle: 'light',
+  // 侧栏风格: light(亮色), dark(暗色)
+  sideStyle: 'dark',
+  // 布局风格: side(默认), top(顶栏导航), mix(混合导航)
+  layoutStyle: 'side',
+  // 侧栏菜单风格: default(默认), mix(双排侧栏)
+  sideMenuStyle: 'default',
+  // 页签风格: default(默认), dot(圆点), card(卡片)
+  tabStyle: 'default',
+  // 路由切换动画
+  transitionName: 'slide-right',
+  // 是否固定顶栏
+  fixedHeader: false,
+  // 是否固定侧栏
+  fixedSidebar: true,
+  // 是否固定主体
+  fixedBody: true,
+  // 内容区域宽度铺满
+  bodyFull: true,
+  // logo 是否自适应宽度
+  logoAutoSize: false,
+  // 侧栏是否彩色图标
+  colorfulIcon: false,
+  // 侧栏是否只保持一个子菜单展开
+  sideUniqueOpen: true,
+  // 是否是色弱模式
+  weakMode: false,
+  // 是否是暗黑模式
+  darkMode: false,
+  // 主题色
+  color: null,
+  // 主页的组件名称
+  homeComponents: [],
+  // 刷新路由时的参数
+  routeReload: null,
+  // 屏幕宽度
+  screenWidth: screenWidth(),
+  // 屏幕高度
+  screenHeight: screenHeight(),
+  // 内容区域宽度
+  contentWidth: contentWidth(),
+  // 内容区域高度
+  contentHeight: contentHeight(),
+  // 是否开启响应式
+  styleResponsive: true
+});
+// 延时操作定时器
+let disableTransitionTimer, updateContentSizeTimer;
+
+/**
+ * 读取缓存配置
+ */
+function getCacheSetting() {
+  try {
+    const value = localStorage.getItem(THEME_STORE_NAME);
+    if (value) {
+      const cache = JSON.parse(value);
+      if (typeof cache === 'object' && cache !== null) {
+        return cache;
+      }
+    }
+  } catch (e) {
+    console.error(e);
+  }
+  return {};
+}
+
+/**
+ * 缓存配置
+ */
+function cacheSetting(key, value) {
+  const cache = getCacheSetting();
+  if (cache[key] !== value) {
+    cache[key] = value;
+    localStorage.setItem(THEME_STORE_NAME, JSON.stringify(cache));
+  }
+}
+
+/**
+ * 开关响应式布局
+ */
+function changeStyleResponsive(styleResponsive) {
+  if (styleResponsive) {
+    document.body.classList.remove(BODY_LIMIT_CLASS);
+  } else {
+    document.body.classList.add(BODY_LIMIT_CLASS);
+  }
+}
+
+/**
+ * 切换色弱模式
+ */
+function changeWeakMode(weakMode) {
+  if (weakMode) {
+    document.body.classList.add(WEAK_CLASS);
+  } else {
+    document.body.classList.remove(WEAK_CLASS);
+  }
+}
+
+/**
+ * 切换主题
+ */
+function changeTheme(value, dark) {
+  return new Promise((resolve, reject) => {
+    try {
+      changeColor(value, dark);
+      resolve();
+    } catch (e) {
+      reject(e);
+    }
+  });
+}
+
+/**
+ * 切换布局时禁用过渡动画
+ */
+function disableTransition() {
+  disableTransitionTimer && clearTimeout(disableTransitionTimer);
+  document.body.classList.add(DISABLES_CLASS);
+  disableTransitionTimer = setTimeout(() => {
+    document.body.classList.remove(DISABLES_CLASS);
+  }, 100);
+}
+
+export default {
+  namespaced: true,
+  state: (() => {
+    const state = { ...DEFAULT_STATE };
+    const cache = getCacheSetting();
+    Object.keys(state).forEach((key) => {
+      if (typeof cache[key] !== 'undefined') {
+        state[key] = cache[key];
+      }
+    });
+    return state;
+  })(),
+  getters: {
+    // 需要 keep-alive 的组件
+    keepAliveInclude(state) {
+      if (!TAB_KEEP_ALIVE || !state.showTabs) {
+        return [];
+      }
+      const components = new Set();
+      const { reloadPath, reloadHome } = state.routeReload || {};
+      state.tabs?.forEach((t) => {
+        const isAlive = t.meta?.keepAlive !== false;
+        const isExclude = KEEP_ALIVE_EXCLUDES.includes(t.path);
+        const isReload = reloadPath && reloadPath === t.fullPath;
+        if (isAlive && !isExclude && !isReload && t.components) {
+          t.components.forEach((c) => {
+            if (typeof c === 'string' && c) {
+              components.add(c);
+            }
+          });
+        }
+      });
+      if (!reloadHome) {
+        state.homeComponents?.forEach((c) => {
+          if (typeof c === 'string' && c) {
+            components.add(c);
+          }
+        });
+      }
+      return Array.from(components);
+    }
+  },
+  mutations: {
+    SET(state, { key, value }) {
+      state[key] = value;
+    }
+  },
+  actions: {
+    setTabs({ commit }, value) {
+      commit('SET', { key: 'tabs', value });
+      //cacheSetting('tabs', value);
+    },
+    setCollapse({ commit, dispatch }, value) {
+      commit('SET', { key: 'collapse', value });
+      dispatch('delayUpdateContentSize', 800);
+    },
+    setSideNavCollapse({ commit, dispatch }, value) {
+      commit('SET', { key: 'sideNavCollapse', value });
+      dispatch('delayUpdateContentSize', 800);
+    },
+    setBodyFullscreen({ commit, dispatch }, value) {
+      disableTransition();
+      commit('SET', { key: 'bodyFullscreen', value });
+      dispatch('delayUpdateContentSize', 800);
+    },
+    setShowTabs({ commit, dispatch }, value) {
+      commit('SET', { key: 'showTabs', value });
+      cacheSetting('showTabs', value);
+      dispatch('delayUpdateContentSize');
+    },
+    setShowFooter({ commit, dispatch }, value) {
+      commit('SET', { key: 'showFooter', value });
+      cacheSetting('showFooter', value);
+      dispatch('delayUpdateContentSize');
+    },
+    setHeadStyle({ commit }, value) {
+      commit('SET', { key: 'headStyle', value });
+      cacheSetting('headStyle', value);
+    },
+    setSideStyle({ commit }, value) {
+      commit('SET', { key: 'sideStyle', value });
+      cacheSetting('sideStyle', value);
+    },
+    setLayoutStyle({ commit, dispatch }, value) {
+      disableTransition();
+      commit('SET', { key: 'layoutStyle', value });
+      cacheSetting('layoutStyle', value);
+      dispatch('delayUpdateContentSize');
+    },
+    setSideMenuStyle({ commit, dispatch }, value) {
+      disableTransition();
+      commit('SET', { key: 'sideMenuStyle', value });
+      cacheSetting('sideMenuStyle', value);
+      dispatch('delayUpdateContentSize');
+    },
+    setTabStyle({ commit }, value) {
+      commit('SET', { key: 'tabStyle', value });
+      cacheSetting('tabStyle', value);
+    },
+    setTransitionName({ commit }, value) {
+      commit('SET', { key: 'transitionName', value });
+      cacheSetting('transitionName', value);
+    },
+    setFixedHeader({ commit }, value) {
+      disableTransition();
+      commit('SET', { key: 'fixedHeader', value });
+      cacheSetting('fixedHeader', value);
+    },
+    setFixedSidebar({ commit }, value) {
+      disableTransition();
+      commit('SET', { key: 'fixedSidebar', value });
+      cacheSetting('fixedSidebar', value);
+    },
+    setFixedBody({ commit }, value) {
+      disableTransition();
+      commit('SET', { key: 'fixedBody', value });
+      cacheSetting('fixedBody', value);
+    },
+    setBodyFull({ commit, dispatch }, value) {
+      commit('SET', { key: 'bodyFull', value });
+      cacheSetting('bodyFull', value);
+      dispatch('delayUpdateContentSize');
+    },
+    setLogoAutoSize({ commit }, value) {
+      disableTransition();
+      commit('SET', { key: 'logoAutoSize', value });
+      cacheSetting('logoAutoSize', value);
+    },
+    setColorfulIcon({ commit }, value) {
+      commit('SET', { key: 'colorfulIcon', value });
+      cacheSetting('colorfulIcon', value);
+    },
+    setSideUniqueOpen({ commit }, value) {
+      commit('SET', { key: 'sideUniqueOpen', value });
+      cacheSetting('sideUniqueOpen', value);
+    },
+    setStyleResponsive({ commit }, value) {
+      changeStyleResponsive(value);
+      commit('SET', { key: 'styleResponsive', value });
+      cacheSetting('styleResponsive', value);
+    },
+    /**
+     * 切换色弱模式
+     * @param value 是否是色弱模式
+     */
+    setWeakMode({ commit }, value) {
+      return new Promise((resolve) => {
+        changeWeakMode(value);
+        commit('SET', { key: 'weakMode', value });
+        cacheSetting('weakMode', value);
+        resolve();
+      });
+    },
+    /**
+     * 切换暗黑模式
+     * @param value 是否是暗黑模式
+     */
+    setDarkMode({ commit, state }, value) {
+      return new Promise((resolve, reject) => {
+        changeTheme(state.color, value)
+          .then(() => {
+            commit('SET', { key: 'darkMode', value });
+            cacheSetting('darkMode', value);
+            resolve();
+          })
+          .catch((e) => {
+            reject(e);
+          });
+      });
+    },
+    /**
+     * 切换主题色
+     * @param value 主题色
+     */
+    setColor({ commit, state }, value) {
+      return new Promise((resolve, reject) => {
+        changeTheme(value, state.darkMode)
+          .then(() => {
+            commit('SET', { key: 'color', value });
+            cacheSetting('color', value);
+            resolve();
+          })
+          .catch((e) => {
+            reject(e);
+          });
+      });
+    },
+    /**
+     * 设置主页路由对应的组件名称
+     * @param components 组件名称
+     */
+    setHomeComponents({ commit }, value) {
+      commit('SET', { key: 'homeComponents', value });
+    },
+    /**
+     * 设置刷新路由信息
+     * @param option 路由刷新参数
+     */
+    setRouteReload({ commit }, value) {
+      commit('SET', { key: 'routeReload', value });
+    },
+    /**
+     * 更新屏幕尺寸
+     */
+    updateScreenSize({ commit, dispatch }) {
+      commit('SET', { key: 'screenWidth', value: screenWidth() });
+      commit('SET', { key: 'screenHeight', value: screenHeight() });
+      dispatch('updateContentSize');
+    },
+    /**
+     * 更新内容区域尺寸
+     */
+    updateContentSize({ commit }) {
+      commit('SET', { key: 'contentWidth', value: contentWidth() });
+      commit('SET', { key: 'contentHeight', value: contentHeight() });
+    },
+    /**
+     * 延时更新内容区域尺寸
+     * @param delay 延迟时间
+     */
+    delayUpdateContentSize({ dispatch }, delay) {
+      updateContentSizeTimer && clearTimeout(updateContentSizeTimer);
+      updateContentSizeTimer = setTimeout(() => {
+        dispatch('updateContentSize');
+      }, delay ?? 100);
+    },
+    /**
+     * 重置设置
+     */
+    resetSetting({ commit, state }) {
+      return new Promise((resolve, reject) => {
+        disableTransition();
+        [
+          'showTabs',
+          'showFooter',
+          'headStyle',
+          'sideStyle',
+          'layoutStyle',
+          'sideMenuStyle',
+          'tabStyle',
+          'transitionName',
+          'fixedHeader',
+          'fixedSidebar',
+          'fixedBody',
+          'bodyFull',
+          'logoAutoSize',
+          'colorfulIcon',
+          'sideUniqueOpen',
+          'styleResponsive',
+          'weakMode',
+          'darkMode',
+          'color'
+        ].forEach((key) => {
+          commit('SET', { key, value: DEFAULT_STATE[key] });
+        });
+        localStorage.removeItem(THEME_STORE_NAME);
+        Promise.all([
+          changeStyleResponsive(state.styleResponsive),
+          changeWeakMode(state.weakMode),
+          changeTheme(state.color, state.darkMode)
+        ])
+          .then(() => {
+            resolve();
+          })
+          .catch((e) => {
+            reject(e);
+          });
+      });
+    },
+    /**
+     * 恢复主题
+     */
+    recoverTheme({ state }) {
+      // 关闭响应式布局
+      if (!state.styleResponsive) {
+        changeStyleResponsive(false);
+      }
+      // 恢复色弱模式
+      if (state.weakMode) {
+        changeWeakMode(true);
+      }
+      // 恢复主题色
+      if (state.color || state.darkMode) {
+        changeTheme(state.color, state.darkMode).catch((e) => {
+          console.error(e);
+        });
+      }
+    },
+    /**
+     * 添加页签或更新相同 key 的页签数据
+     * @param data 页签数据
+     */
+    tabAdd({ dispatch, state }, data) {
+      if (Array.isArray(data)) {
+        data.forEach((d) => {
+          dispatch('tabAdd', d);
+        });
+        return;
+      }
+      const i = state.tabs.findIndex((d) => d.key === data.key);
+      if (i === -1) {
+        dispatch('setTabs', state.tabs.concat([data]));
+      } else if (data.fullPath !== state.tabs[i].fullPath) {
+        dispatch(
+          'setTabs',
+          state.tabs
+            .slice(0, i)
+            .concat([data])
+            .concat(state.tabs.slice(i + 1))
+        );
+      }
+    },
+    /**
+     * 关闭页签
+     * @param key 页签 key
+     */
+    async tabRemove({ dispatch, state }, { key, active }) {
+      if (window.__POWERED_BY_QIANKUN__) {
+        return Vue.prototype.$portalStore?.dispatch('theme/tabRemove', {
+          key,
+          active
+        });
+      } else {
+        const i = state.tabs.findIndex(
+          (t) => t.key === key || t.fullPath === key
+        );
+        if (i === -1) {
+          return {};
+        }
+        const t = state.tabs[i];
+        if (!t.closable) {
+          return Promise.reject();
+        }
+        const path = state.tabs[i - 1]?.fullPath;
+        dispatch(
+          'setTabs',
+          state.tabs.filter((_d, j) => j !== i)
+        );
+        return t.key === active ? { path, home: !path } : {};
+      }
+    },
+    /**
+     * 关闭左侧页签
+     */
+    async tabRemoveLeft({ dispatch, state }, { key, active }) {
+      let index = -1; // 选中页签的 index
+      for (let i = 0; i < state.tabs.length; i++) {
+        if (state.tabs[i].key === active) {
+          index = i;
+        }
+        if (state.tabs[i].key === key) {
+          if (i === 0) {
+            break;
+          }
+          const temp = state.tabs.filter((d, j) => !d.closable && j < i);
+          if (temp.length === i + 1) {
+            break;
+          }
+          const path = index === -1 ? void 0 : state.tabs[i].fullPath;
+          dispatch('setTabs', temp.concat(state.tabs.slice(i)));
+          return { path };
+        }
+      }
+      return Promise.reject();
+    },
+    /**
+     * 关闭右侧页签
+     */
+    async tabRemoveRight({ dispatch, state }, { key, active }) {
+      if (state.tabs.length) {
+        let index = -1; // 选中页签的 index
+        for (let i = 0; i < state.tabs.length; i++) {
+          if (state.tabs[i].key === active) {
+            index = i;
+          }
+          if (state.tabs[i].key === key) {
+            if (i === state.tabs.length - 1) {
+              return Promise.reject();
+            }
+            const temp = state.tabs.filter((d, j) => !d.closable && j > i);
+            if (temp.length === state.tabs.length - i - 1) {
+              return Promise.reject();
+            }
+            const path = index === -1 ? state.tabs[i].fullPath : void 0;
+            dispatch(
+              'setTabs',
+              state.tabs
+                .slice(0, i + 1)
+                .concat(state.tabs.filter((d, j) => !d.closable && j > i))
+            );
+            return { path };
+          }
+        }
+        // 主页时关闭全部
+        const temp = state.tabs.filter((d) => !d.closable);
+        if (temp.length !== state.tabs.length) {
+          dispatch('setTabs', temp);
+          return { home: index !== -1 };
+        }
+      }
+      return Promise.reject();
+    },
+    /**
+     * 关闭其它页签
+     */
+    async tabRemoveOther({ dispatch, state }, { key, active }) {
+      let index = -1; // 选中页签的 index
+      let path; // 关闭后跳转的 path
+      const temp = state.tabs.filter((d, i) => {
+        if (d.key === active) {
+          index = i;
+        }
+        if (d.key === key) {
+          path = d.fullPath;
+        }
+        return !d.closable || d.key === key;
+      });
+      if (temp.length === state.tabs.length) {
+        return Promise.reject();
+      }
+      dispatch('setTabs', temp);
+      if (index === -1) {
+        return {};
+      }
+      return key === active ? {} : { path, home: !path };
+    },
+    /**
+     * 关闭全部页签
+     * @param active 选中页签的 key
+     */
+    async tabRemoveAll({ dispatch, state }, active) {
+      const t = state.tabs.find((d) => d.key === active);
+      const home = typeof t !== 'undefined' && t.closable === true; // 是否跳转主页
+      const temp = state.tabs.filter((d) => !d.closable);
+      if (temp.length === state.tabs.length) {
+        return Promise.reject();
+      }
+      dispatch('setTabs', temp);
+      return { home };
+    },
+    /**
+     * 修改页签
+     * @param data 页签数据
+     */
+    tabSetItem({ dispatch, state }, data) {
+      let i = -1;
+      if (data.key) {
+        i = state.tabs.findIndex((d) => d.key === data.key);
+      } else if (data.fullPath) {
+        i = state.tabs.findIndex((d) => d.fullPath === data.fullPath);
+      } else if (data.path) {
+        i = state.tabs.findIndex((d) => d.path === data.path);
+      }
+      if (i !== -1) {
+        const item = { ...state.tabs[i] };
+        if (data.title) {
+          item.title = data.title;
+        }
+        if (typeof data.closable === 'boolean') {
+          item.closable = data.closable;
+        }
+        if (data.components) {
+          item.components = data.components;
+        }
+        dispatch(
+          'setTabs',
+          state.tabs
+            .slice(0, i)
+            .concat([item])
+            .concat(state.tabs.slice(i + 1))
+        );
+      }
+    }
+  }
+};

+ 198 - 0
src/store/modules/user.js

@@ -0,0 +1,198 @@
+/**
+ * 登录状态管理
+ */
+import { formatMenus, toTreeData, formatTreeData } from 'ele-admin';
+import { USER_MENUS } from '@/config/setting';
+import { getResourcesTree } from '@/api/layout';
+import { SYSTEM_NAME } from '@/config/setting';
+import Vue from 'vue';
+const formatRouter = (list) => {
+  let menuList = []; // menuType
+  let authorities = [];
+
+  const fn = (list) => {
+    let arr = [];
+    for (const p of list) {
+      if (p.menuType === 2) {
+        // p.children = [];
+        // authorities.push(p);
+        p.hide = true;
+      }
+      //  else {
+      if (p.children?.length) {
+        p.children = fn(p.children);
+      } else {
+        p.children = [];
+      }
+      arr.push(p);
+      // }
+    }
+
+    return arr;
+  };
+
+  menuList = fn(list);
+
+  return { menuList, authorities };
+};
+
+export default {
+  namespaced: true,
+  state: {
+    // 当前登录用户信息
+    info: null,
+    // 当前登录用户的菜单
+    menus: null,
+    // 当前登录用户的权限
+    authorities: [],
+    // 当前登录用户的权限路由
+    authoritiesRouter: [],
+    // 当前登录用户的角色
+    roles: []
+  },
+  mutations: {
+    // 设置登录用户的信息
+    setUserInfo(state, info) {
+      state.info = info;
+    },
+    // 设置登录用户的菜单
+    setMenus(state, menus) {
+      state.menus = menus;
+    },
+    // 设置登录用户的权限
+    setAuthorities(state, authorities) {
+      state.authorities = authorities;
+    },
+    // 设置登录用户的权限路由
+    setAuthoritiesRouter(state, authoritiesRouter) {
+      state.authoritiesRouter = authoritiesRouter;
+    },
+    // 设置登录用户的角色
+    setRoles(state, roles) {
+      state.roles = roles;
+    }
+  },
+  actions: {
+    /**
+     * 请求用户信息、权限、角色、菜单
+     */
+    // async fetchUserInfo({ commit }) {
+    //   const result = await getResourcesTree().catch(() => {});
+    //   if (!result) {
+    //     return {};
+    //   }
+    //   // 用户信息
+    //   commit('setUserInfo', result);
+    //   // 用户权限
+    //   const authorities =
+    //     result.authorities
+    //       ?.filter((d) => !!d.authority)
+    //       ?.map((d) => d.authority) ?? [];
+    //   commit('setAuthorities', authorities);
+    //   // 用户角色
+    //   const roles = result.roles?.map((d) => d.roleCode) ?? [];
+    //   commit('setRoles', roles);
+    //   // 用户菜单, 过滤掉按钮类型并转为 children 形式
+    //   const { menus, homePath } = formatMenus(
+    //     USER_MENUS ??
+    //       toTreeData({
+    //         data: result.authorities?.filter((d) => d.menuType !== 1),
+    //         idField: 'menuId',
+    //         parentIdField: 'parentId'
+    //       })
+    //   );
+    //   commit('setMenus', menus);
+    //   console.log('menus',menus, 'homePath',homePath)
+    //   return { menus, homePath };
+    // },
+    //动态路由
+    async fetchUserInfo({ commit }) {
+      const result = await getResourcesTree().catch(() => {});
+      const list = result.filter((i) => i.path === `/page-${SYSTEM_NAME}`);
+      if (!list.length) {
+        return {};
+      }
+
+      const { menuList, authorities } = formatRouter(list[0].children);
+
+      // 用户权限
+      // const authorities =
+      //   result.authorities
+      //     ?.filter((d) => !!d.authority)
+      //     ?.map((d) => d.authority) ?? [];
+
+      commit('setAuthorities', authorities);
+      // 用户角色
+      // const roles = result.roles?.map((d) => d.roleCode) ?? [];
+      // commit('setRoles', roles);
+      // 用户菜单, 过滤掉按钮类型并转为 children 形式
+      const { menus, homePath } = formatMenus(
+        USER_MENUS ??
+          toTreeData({
+            data: menuList,
+            idField: 'id',
+            parentIdField: 'parentId'
+          })
+      );
+      // 用户路由按钮
+      const { menus: authoritiesRouter } = formatMenus(
+        USER_MENUS ??
+          toTreeData({
+            data: authorities.filter((i) => i.path),
+            idField: 'id',
+            parentIdField: 'parentId'
+          })
+      );
+      console.log('menus--', menus);
+      commit('setMenus', menus);
+      commit('setAuthoritiesRouter', authoritiesRouter);
+      // const menus = result;
+      // const homePath = '/dashboard/workplace';
+      return {
+        menus,
+        homePath,
+        authoritiesRouter
+      };
+    },
+
+    /**
+     * 更新用户信息
+     */
+    setInfo({ commit }, value) {
+      commit('setUserInfo', value);
+    },
+    /**
+     * 更新菜单数据
+     */
+    setMenus({ commit }, value) {
+      commit('setMenus', value);
+    },
+    /**
+     * 更新菜单的badge
+     */
+    setMenuBadge({ commit, state }, { path, value, color }) {
+      if (window.__POWERED_BY_QIANKUN__) {
+        Vue.prototype.$portalStore?.dispatch('user/setMenuBadge', {
+          path: `/page-${SYSTEM_NAME}${path}`,
+          value,
+          color
+        });
+      } else {
+        const menus = formatTreeData(state.menus, (m) => {
+          if (path === m.path) {
+            return {
+              ...m,
+              meta: {
+                ...m.meta,
+                badge: value,
+                badgeColor: color
+              }
+            };
+          }
+          return m;
+        });
+        commit('setMenus', menus);
+      }
+    }
+  }
+};

+ 20 - 0
src/store/plugins.js

@@ -0,0 +1,20 @@
+import vue from 'vue';
+
+export const createMutationObserve = ({ modules = [] }) => {
+  return (store) => {
+    store.subscribe((mutation, state) => {
+      // 每次 mutation 之后调用
+      // mutation 的格式为 { type, payload }
+      console.log(mutation, 'mutation');
+      if (
+        modules?.length &&
+        modules.find((i) => mutation?.type?.startsWith(i + '/')) &&
+        mutation.payload.toPort
+      ) {
+        console.log(vue.prototype.$portalStore, 'vue.prototype.$portalStore');
+        vue.prototype.$portalStore?.commit &&
+          vue.prototype.$portalStore?.commit(mutation.type, mutation.payload);
+      }
+    });
+  };
+};

+ 17 - 0
src/styles/transition/font.scss

@@ -0,0 +1,17 @@
+@font-face {
+  font-family: '优设标题黑';  // 自定义字体的名称
+  src: url('@/assets/fonts/优设标题黑.ttf') format('truetype');  // 自定义字体文件的路径
+}
+@font-face {
+  font-family: 'LCD2B';  // 自定义字体的名称
+  src: url('@/assets/fonts/LCD2B___.TTF') format('truetype');  // 自定义字体文件的路径
+}
+@font-face {
+  font-family: 'AlibabaPuHuiTi';  // 自定义字体的名称
+  src: url('@/assets/fonts/AlibabaPuHuiTi-2-45-Light.ttf') format('truetype');  // 自定义字体文件的路径
+}
+@font-face {
+  font-family: 'DS-DIGIT';  // 自定义字体的名称
+  src: url('@/assets/fonts/DS-DIGIT.TTF') format('truetype');  // 自定义字体文件的路径
+}
+

+ 75 - 0
src/views/bpm/vis-page/dv-border-content.vue

@@ -0,0 +1,75 @@
+<script>
+export default {
+  name: "dv-border-box",
+  props: {
+    objD: {
+      type: Object,
+      default: () => {
+      }
+    }
+  },
+  data() {
+    return {}
+  },
+  created() {
+  }
+}
+</script>
+
+<template>
+  <div class="box-content">
+    <div class="box-top">
+      <span class="box-top-name">{{ objD.titleName }}</span>
+      <span class="box-top-unit">{{ objD.titleUnit }}</span>
+    </div>
+    <div class="box-footer">
+      <span class="box-footer-value">{{ objD.value }}</span>
+    </div>
+
+  </div>
+</template>
+
+<style scoped lang="scss">
+.box-content {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  color: #ffffff;
+
+  .box-top {
+    width: 100%;
+    height:17%;
+    display: flex;
+    justify-content: center;
+    align-items: flex-end;
+    .box-top-name {
+      display: inline-block;
+      font-size: 0.8rem;
+      letter-spacing:2px;
+      font-weight: bold;
+     // color: #f4d29c;
+    }
+
+    .box-top-unit {
+      display: inline-block;
+      font-size:0.55rem;
+      transform: translate(0px, -30%);
+      letter-spacing:1px;
+      font-weight: bold;
+    }
+  }
+
+  .box-footer {
+    flex: 1;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    .box-footer-value {
+      font-family: 'LCD2B';
+      display: inline-block;
+      font-size: 2rem;
+    }
+  }
+}
+</style>

+ 698 - 0
src/views/bpm/vis-page/page2.vue

@@ -0,0 +1,698 @@
+<template>
+  <vue-fullscreen
+    class="box-container"
+    v-cloak
+    v-model="isFullscreen"
+    fullscreenClass="box-container"
+    :exit-on-click-wrapper="false"
+  >
+    <div class="box-container" v-cloak>
+      <div class="box-header">
+        <div class="logo">
+<!--          <img style="width: 20% ;height: 100%;transform: translate(51%,15%)" src="../../../assets/TGLOGO.png" alt="">-->
+        </div>
+        <div class="title">
+          采购仓储数据看板
+        </div>
+        <div class="time">
+          <span
+            style="color: #7fa7ce;margin: 5px 10px 15px 0;cursor: pointer" @click.passive="onFullscreen">
+            <i v-if="isFullscreen" title="取消全屏" class="el-icon-_screen-restore"></i>
+            <i v-else title="全屏" class="el-icon-_screen-full"></i>
+          </span>
+          <div style="margin-right: 10px">
+            <span style="color: #7fa7ce">{{ date }}</span>
+            <span style="color: rgb(210 215 221);  padding: 0 5px;">{{ time }}</span>
+            <span style="color: #7fa7ce;">{{ week }}
+          </span>
+          </div>
+
+        </div>
+      </div>
+
+
+      <div class="box-middle">
+        <div class="box-middle-content" style="width: 35%">
+          <div class="box-middle-content-left">
+            <span>月度总金额</span>
+            <span>(万元)</span>
+          </div>
+          <div class="box-middle-content-right">
+            <template v-for="item in MonthlyAmount">
+              <span style="display: flex;align-content: flex-end"
+                    v-if="item=='.'">{{ '.' }}</span>
+              <span v-else class="box-border8">{{ item }}</span>
+            </template>
+
+          </div>
+        </div>
+        <div class="box-middle-content">
+          <div class="box-middle-content-left">
+            <span>年度总金额</span>
+            <span>(万元)</span>
+          </div>
+          <div class="box-middle-content-right">
+            <template v-for="item in YearlyAmount">
+              <span style="display: flex;align-content: flex-end;"
+                    v-if="item=='.'">{{ '.' }}</span>
+              <span v-else class="box-border8" style=" width: 10%;">{{ item }}</span>
+            </template>
+          </div>
+        </div>
+
+      </div>
+      <div class="box-footer">
+        <div class="box-footer-right">
+          <div class="box-echarts-top">
+            <span class="box-top-name">月度采购明细</span>
+          </div>
+          <dv-scroll-board v-if="isFlag" :config="config" style="width:92%;height:80%;transform: translate(5%,7%);"/>
+        </div>
+        <div class="box-footer-left">
+          <div class="box-echarts">
+            <div class="box-echarts-top">
+              <span class="box-top-name">年度采购统计</span>
+              <span class="box-top-unit">(万元)</span>
+            </div>
+            <div class="monthly_sales_volume">
+              <div v-if="isFlag" id="monthly_sales_volume" ref="monthly_sales_volume"></div>
+            </div>
+
+            <!--            <div style="position: fixed; transform: translate(13%, 110%);">-->
+            <!--              <div style="color:#03c391">-->
+            <!--                <span>{{ '累计签单金额 ' }}</span>-->
+            <!--                <span style="font-weight: bold">{{ borderData[0].value }}</span>-->
+            <!--              </div>-->
+            <!--              <div style="color:#ea5082">-->
+            <!--                <span>{{ '累计交货金额 ' }}</span>-->
+            <!--                <span style="font-weight: bold">{{ borderData[1].value }}</span>-->
+            <!--              </div>-->
+            <!--              <div style="color:#faab46">-->
+            <!--                <span>{{ '累计销售金额 ' }}</span>-->
+            <!--                <span style="font-weight: bold">{{ borderData[2].value }}</span>-->
+            <!--              </div>-->
+            <!--            </div>-->
+          </div>
+        </div>
+
+      </div>
+    </div>
+  </vue-fullscreen>
+
+
+</template>
+
+<script>
+import dvBorderContent from "./dv-border-content.vue";
+import * as echarts from 'echarts'
+import {component} from 'vue-fullscreen'
+import {
+  getMonthlyPurchaseDetailAPI,
+  getYearlyPurchaseAmountAPI,
+  getMonthlyPurchaseAmountAPI,
+  getYearlyPurchaseStatisticAPI,
+} from "@/api/bpm/visPage";
+import {toggleFullscreen} from "ele-admin";
+
+
+export default {
+  name: 'index',
+  components: {
+    dvBorderContent,
+    VueFullscreen: component
+  },
+  computed: {
+    contentWidth() {
+
+      return this.$store.state.theme.contentWidth;
+    }
+  },
+  watch: {
+    isFullscreen: {
+      handler() {
+        this.isFlag = false
+        this.$nextTick(() => {
+          let {clientWidth: deviceWidth, clientHeight: deviceHeight} = document.documentElement;
+          let eleAdminHeaderHeight = (!this.isFullscreen && document.getElementsByClassName('ele-admin-header')[0]?.offsetHeight) || 0;
+          let eleAdminSidebarWidth = (!this.isFullscreen && document.getElementsByClassName('ele-admin-sidebar')[0]?.offsetWidth) || 0;
+          let eleAdminTabsWidth = (!this.isFullscreen && document.getElementsByClassName('ele-admin-tabs')[0]?.offsetHeight) || 0;
+
+          const setContainerSize = (item) => {
+            item.style.height = deviceHeight - eleAdminHeaderHeight - eleAdminTabsWidth + 'px';
+            item.style.width = deviceWidth - eleAdminSidebarWidth + 'px';
+          };
+
+          let boxContainer = [...document.getElementsByClassName('box-container')];
+          boxContainer.forEach(setContainerSize);
+          document.documentElement.style.fontSize = this.isFullscreen ? '24px' : '16px'
+
+          this.isFlag = true
+          this.$nextTick(() => {
+
+            let chartDom = this.$refs.monthly_sales_volume;
+            this.salesChart = echarts.init(chartDom);
+            this.getMonthlySalesStatistic()
+          })
+        })
+      }, immediate: true
+    },
+    contentWidth: {
+      handler() {
+        clearTimeout(this.resizeTimer);
+        this.isFlag = false
+        this.resizeTimer = setTimeout(() => {
+          this.$nextTick(() => {
+            let {clientWidth: deviceWidth, clientHeight: deviceHeight} = document.documentElement;
+            let eleAdminHeaderHeight = (!this.isFullscreen && document.getElementsByClassName('ele-admin-header')[0]?.offsetHeight) || 0;
+            let eleAdminSidebarWidth = (!this.isFullscreen && document.getElementsByClassName('ele-admin-sidebar')[0]?.offsetWidth) || 0;
+            let eleAdminTabsWidth = (!this.isFullscreen && document.getElementsByClassName('ele-admin-tabs')[0]?.offsetHeight) || 0;
+
+            const setContainerSize = (item) => {
+              item.style.height = deviceHeight - eleAdminHeaderHeight - eleAdminTabsWidth + 'px';
+              item.style.width = deviceWidth - eleAdminSidebarWidth + 'px';
+            };
+
+            let boxContainer = [...document.getElementsByClassName('box-container')];
+            boxContainer.forEach(setContainerSize);
+            document.documentElement.style.fontSize = this.isFullscreen ? '24px' : '16px'
+            this.isFlag = true
+            this.$nextTick(() => {
+              let chartDom = this.$refs.monthly_sales_volume;
+              this.salesChart = echarts.init(chartDom);
+              this.getMonthlySalesStatistic()
+            })
+
+
+          })
+        }, 300)
+      }, immediate: true
+    },
+  },
+
+  data() {
+    return {
+      isFullscreen: false, // 是否是全屏
+      isFlag: false, // 是否展示图表
+      resizeTimer: null,
+      timer: null,
+      date: '',
+      time: '',
+      week: '',
+      MonthlyAmount: 0,
+      YearlyAmount: 0,
+      salesChart: null,
+      monthlySalesVolumeOption: {
+        legend: {
+          left: 'center',
+          selectedMode: false,
+          bottom: '85%',
+          itemGap: 50,
+          textStyle: {
+            fontSize: '0.7rem',
+            color: '#fff',
+            lineHeight: 0,
+            padding: [10, 0, 0, 0]
+          }
+        },
+        grid: {
+          bottom: '5%',  // 组件离容器底部的距离
+          containLabel: true // 包含坐标轴的标签
+        },
+        xAxis: {
+          type: 'category',
+          data: [],
+          axisLabel: {
+            interval: 0,
+            formatter: function (value, index) {
+              let month = value.split('-')
+              return month[1] + '月';
+            },
+            textStyle: {
+              color: '#fff', // 修改字体颜色为红色
+              fontSize: '0.7rem',
+              fontFamily: 'AlibabaPuHuiTi' // 修改字体为Arial
+            }
+          },
+          axisLine: {},
+          axisTick: {}
+        },
+        yAxis: {
+          type: 'value',
+          axisLabel: {
+            textStyle: {
+              color: '#fff', // 修改字体颜色为红色
+              fontSize: '0.7rem',
+              fontFamily: 'AlibabaPuHuiTi' // 修改字体为Arial
+            }
+          }
+        },
+        series: []
+      },
+      tableHeader: ['客户代号', '销售订单号', '采购订单', '到货情况', '入库情况', '质检状态', '合格率'],
+      config: {
+        header: [],
+        data: [],
+        align: ['center', 'center', 'center', 'center', 'center', 'center', 'center'],
+        headerBGC: '#031d42',
+        columnWidth: [110, 200],
+        headerHeight: 20,
+        oddRowBGC: '#031d42',
+        evenRowBGC: '#031d42',
+        waitTime: 5000,
+        rowNum: 6
+      },
+      //1已检0未检
+      qcStatusList: [
+        {
+          dictKey: 1,
+          dictValue: '已检'
+        },
+        {
+          dictKey: 0,
+          dictValue: '未检'
+        },
+      ],
+      // 1在途2提前3按时4延期
+      receiveStatusList: [
+        {
+          dictKey: 1,
+          dictValue: '在途'
+        },
+        {
+          dictKey: 2,
+          dictValue: '提前'
+        },
+        {
+          dictKey: 3,
+          dictValue: '按时'
+        },
+        {
+          dictKey: 4,
+          dictValue: '延期'
+        }
+      ],
+      //入库状态 1已入库0未入库	integer(int32)
+      inStoreStatusList: [
+        {
+          dictKey: 1,
+          dictValue: '已入库'
+        },
+        {
+          dictKey: 0,
+          dictValue: '未入库'
+        }
+      ],
+
+    };
+  },
+  created() {
+    this.updateTimer = setInterval(this.updateTime, 1000)
+  },
+  mounted() {
+    this.getSalesFinishList()
+    this.timer = setInterval(() => {
+      this.getSalesFinishList()
+    }, 3600000)
+  },
+// 在组件销毁时清除定时器
+  beforeDestroy() {
+    clearInterval(this.updateTimer)
+  },
+  methods: {
+    /* 全屏切换 */
+    onFullscreen() {
+      this.isFullscreen = !this.isFullscreen
+    },
+    //获取年度采购统计
+    async getMonthlySalesStatistic() {
+      let data = await getYearlyPurchaseStatisticAPI()
+      let series = [
+        {
+          field: 'contractPrice',
+          name: '采购订单金额',
+          data: [],
+          type: 'bar',
+          itemStyle: {
+            color: '#03c391'
+          }
+        },
+        {
+          field: 'qcInStorePrice',
+          name: '合格入库金额',
+          data: [],
+          type: 'bar',
+          itemStyle: {
+            color: '#ea5082'
+          }
+        }
+      ]
+      this.monthlySalesVolumeOption.xAxis.data = data.map(item => item.monthStr)
+      series.forEach(item => {
+        item.data = data.map(i => i[item.field])
+      })
+      this.monthlySalesVolumeOption.series = series
+      this.salesChart.setOption(this.monthlySalesVolumeOption)
+
+    },
+
+    // 获取销售完成列表数据
+    async getSalesFinishList() {
+      // customerMark	客户代号	string
+      // inStoreStatus	入库状态 1已入库0未入库	integer(int32)
+      // purchaseOrderCode	采购订单	string
+      // qcRate	合格率	number
+      // qcStatus	质检状态 1已检0未检	integer(int32)
+      // receiveStatus	到货情况 1在途2提前3按时4延期	integer(int32)
+      // saleOrderCode	销售订单	string
+
+      let data = await getMonthlyPurchaseDetailAPI()
+      this.config = {
+        header: this.tableHeader.map(item => `<div style="color: #f4d29c;font-size: 0.9rem;font-weight: bold">${item}</div>`),
+        data: data.map(item => {
+          let list = []
+          for (let i in item) {
+            let div = ''
+            if (i === 'customerMark') {
+              div = `<div class="white" style="font-size: 0.8rem;">${item[i] || '-'}</div>`
+              list[0] = div
+            }
+            if (i === 'saleOrderCode') {
+              div = `<div class="white" style="font-size: 0.8rem;">${item[i] || '-'}</div>`
+              list[1] = div
+            }
+            if (i === 'purchaseOrderCode') {
+              div = `<div class="white" style="font-size: 0.8rem;">${item[i] || '-'}</div>`
+              list[2] = div
+            }
+            if (i === 'receiveStatus') {
+              div = `<div class="white" style="font-size: 0.8rem;">${this.getValue('receiveStatus', item[i])}</div>`
+              list[3] = div
+
+            }
+            if (i === 'inStoreStatus') {
+              div = `<div class="white" style="font-size: 0.8rem;">${this.getValue('inStoreStatus', item[i])}</div>`
+              list[4] = div
+
+            }
+            if (i === 'qcStatus') {
+              div = `<div class="white" style="font-size: 0.8rem;">${this.getValue('qcStatus', item[i])}</div>`
+              list[5] = div
+            }
+            if (i === 'qcRate') {
+              div = `<div class="white" style="font-size: 0.8rem;">${item[i] + '%'}</div>`
+              list[6] = div
+            }
+          }
+          return list
+        }) ?? [],
+        align: ['center', 'center', 'center', 'center', 'center', 'center', 'center'],
+        headerBGC: '#031d42',
+        columnWidth: [],
+        headerHeight: 30,
+        oddRowBGC: '#031d42',
+        evenRowBGC: '#031d42',
+        waitTime: 5000,
+        rowNum: 6,
+      }
+
+      /*年度总金额*/
+      this.YearlyAmount = await getYearlyPurchaseAmountAPI()
+      this.YearlyAmount = (this.YearlyAmount + '').includes('.') ? ((this.YearlyAmount.toFixed(2)) + '').padStart(8, 0) : (this.YearlyAmount + '').padStart(8, 0)
+      /*月度总金额*/
+      this.MonthlyAmount = await getMonthlyPurchaseAmountAPI()
+      this.MonthlyAmount = (this.MonthlyAmount + '').includes('.') ? ((this.MonthlyAmount.toFixed(2)) + '').padStart(7, 0) : (this.MonthlyAmount + '').padStart(7, 0)
+
+    },
+    // 获取自定义字典值
+    getValue(name, key) {
+      let find = this[name + 'List'].find(item => item.dictKey == key) || {}
+      return find.dictValue || ''
+    },
+    //实时更新日期
+    updateTime() {
+      let now = new Date();
+      let hours = now.getHours();
+      let minutes = now.getMinutes();
+      let seconds = now.getSeconds();
+      this.time = hours.toString().padStart(2, '0') + ":" +
+        minutes.toString().padStart(2, '0') + ":" +
+        seconds.toString().padStart(2, '0');
+
+      let year = now.getFullYear();
+      let month = now.getMonth() + 1;
+      let day = now.getDate();
+      this.date = year + '年' + month + '月' + day + '日'
+
+      let weekInfo = {
+        1: '一',
+        2: '二',
+        3: '三',
+        4: '四',
+        5: '五',
+        6: '六',
+        0: '日',
+      }
+      this.week = '星期' + weekInfo[now.getDay()]
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+[v-cloak] {
+  display: none;
+}
+
+.box-container {
+  font-size: 16px;
+  font-family: 'AlibabaPuHuiTi';
+
+  background: #031d42;
+  display: flex;
+  flex-direction: column;
+
+  .box-header {
+    background-image: url("@/assets/border2.png");
+    background-repeat: no-repeat;
+    background-size: 100% 100%;
+    display: flex;
+    width: 100% !important;
+    height: 10%;
+    justify-content: space-between;
+
+    .title {
+      font-family: '优设标题黑';
+      font-size: 2.85rem;
+      flex: 1;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      color: #fff;
+      transform: translate(2%, -10%);
+      letter-spacing: 0.4rem;
+    }
+
+    .logo {
+      display: inline-block;
+      width: 20%;
+    }
+
+    .time {
+      width: 20%;
+      display: flex;
+      align-items: center;
+      justify-content: space-around;
+      flex-direction: column;
+      align-items: flex-end;
+      justify-content: flex-start;
+      font-size: 0.8rem;
+    }
+  }
+
+  .box-middle {
+    width: 100% !important;
+    height: 10% !important;
+    display: flex;
+    justify-content: center;
+    margin: 5px 0 10px 0;
+
+    .box-middle-content {
+      width: 45%;
+      height: 100%;
+      background-image: url("@/assets/png/border6.png");
+      background-repeat: no-repeat;
+      background-size: 100% 100%;
+      margin: 0 28px 10px 0;
+      display: flex;
+      align-items: center;
+
+      .box-middle-content-left {
+        width: 30%;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        height: 100%;
+        font-size: 1rem;
+        font-weight: bold;
+        color: #fff;
+      }
+
+      .box-middle-content-right {
+        flex: 1;
+        display: flex;
+        //font-family: 'LCD2B';
+        font-size: 3rem;
+        color: #fff;
+        justify-content: space-evenly;
+        height: 61%;
+
+        .box-border8 {
+          background-image: url("@/assets/png/border8.png");
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+          width: 12%;
+          display: flex;
+          font-size: 3rem;
+          justify-content: center;
+          font-family: 'DS-DIGIT';
+          align-items: center;
+        }
+
+        .border-box-content {
+        }
+      }
+    }
+
+
+  }
+
+  .box-footer {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+
+    .box-footer-left {
+      width: 100%;
+      height: 50%;
+
+      .box-echarts {
+        width: 100%;
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        background-image: url("@/assets/png/border5.png");
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+
+        .box-echarts-top {
+          background-image: url("@/assets/png/border7.png");
+          background-repeat: no-repeat;
+          background-size: 94% 100%;
+          height: 15.5%;
+
+          width: 100%;
+          display: flex;
+          align-items: center;
+          /* justify-content: space-around; */
+          transform: translate(2%, 20%);
+          color: #fff;
+
+          .box-top-name {
+            font-family: '优设标题黑';
+            transform: translateX(40%);
+            display: inline-block;
+            font-size: 1.8rem;
+
+          }
+
+          .box-top-unit {
+            font-family: '优设标题黑';
+            display: inline-block;
+            font-size: 1rem;
+            transform: translate(150%, 20%);
+            letter-spacing: 0.9px;
+
+          }
+        }
+
+        .monthly_sales_volume {
+          transform: translateX(-7%);
+          flex: 1;
+          //width: 100%;
+          //height: 100%;
+          #monthly_sales_volume {
+            width: 113%;
+            height: 100%;
+          }
+        }
+
+        .monthly_output {
+          transform: translateX(-7%);
+          flex: 1;
+
+          #monthly_output {
+
+            width: 113%;
+            height: 100%;
+            //width: 100%;
+            //height: 100%;
+          }
+        }
+
+      }
+    }
+
+    .box-footer-right {
+      width: 100%;
+      height: 50%;
+      background-image: url("@/assets/png/border5.png");
+      background-repeat: no-repeat;
+      background-size: 100% 100%;
+
+      .box-echarts-top {
+        background-image: url("@/assets/png/border7.png");
+        background-repeat: no-repeat;
+        background-size: 94% 100%;
+        height: 15.5%;
+        width: 100%;
+        display: flex;
+        align-items: center;
+        transform: translate(2%, 20%);
+        /* justify-content: space-around; */
+
+        color: #fff;
+        font-size: 1.1rem;
+        letter-spacing: 2px;
+
+        .box-top-name {
+          font-family: '优设标题黑';
+          transform: translateX(40%);
+          display: inline-block;
+          font-size: 1.8rem;
+
+        }
+      }
+    }
+  }
+}
+
+
+</style>
+<style>
+.white {
+  color: #ffffff;
+}
+
+.yellow {
+  color: #ffd16c;
+}
+
+.green {
+  color: green;
+}
+
+.red {
+  color: red;
+}
+</style>