| src/api/procurementManagement/paymentLedger.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/salesManagement/indicatorStats.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/components/PIMTable/PIMTable.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/layout/components/Navbar.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/store/modules/user.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/login.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/procurementManagement/paymentLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/procurementManagement/procurementLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/salesManagement/indicatorStats/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/salesManagement/receiptPaymentLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/salesManagement/salesLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/system/dept/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/system/user/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/procurementManagement/paymentLedger.js
@@ -4,16 +4,17 @@ // å页æ¥è¯¢ export function paymentLedgerList(query) { return request({ url: "/purchase/paymentRegistration/paymentLedgerList", url: "/purchase/paymentRegistration/supplierNameListPage", method: "get", params: query, }); } // å页æ¥è¯¢ export function paymentRecordList(supplierId) { export function paymentRecordList(query) { return request({ url: "/purchase/paymentRegistration/getPaymentRecordList/" + supplierId, url: "/purchase/paymentRegistration/supplierNameListPageDetails", method: "get", params: query, }); } src/api/salesManagement/indicatorStats.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,20 @@ // ææ ç»è®¡é¡µé¢æ¥å£ import request from "@/utils/request"; // 头é¨ç»è®¡æ¥å£ export function getTotalStatistics(query) { return request({ url: "/metricStatistics/total", method: "get", params: query, }); } // æ±ç¶å¾æ°æ®æ¥å£ export function getStatisticsTable(query) { return request({ url: "/metricStatistics/statisticsTable", method: "get", params: query, }); } src/components/PIMTable/PIMTable.vue
@@ -214,6 +214,7 @@ </el-table-column> </el-table> <pagination v-if="isShowPagination" :total="page.total" :layout="page.layout" :page="page.current" @@ -276,6 +277,10 @@ isSelection: { type: Boolean, default: false, }, isShowPagination: { type: Boolean, default: true, }, isShowSummary: { type: Boolean, @@ -438,19 +443,6 @@ text-overflow: ellipsis; padding-right: 0 !important; padding-left: 0 !important; } .pim-table-header-cell { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; text-align: center; } .pim-table-header-title { font-weight: 500; } .pim-table-header-extra :deep(.el-input), src/layout/components/Navbar.vue
@@ -6,23 +6,6 @@ <breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" /> </div> <!-- <top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />--> <div class="center-menu"> <span class="label">{{ userStore.currentFactoryName }}</span> <el-dropdown @command="handleFactoryChange" class="right-menu-item hover-effect" trigger="click"> <div> <el-icon size="20"> <Switch /> </el-icon> </div> <template #dropdown> <el-dropdown-menu> <el-dropdown-item v-for="item in factoryList" :key="item.deptId" :command="item"> {{ item.deptName }} </el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </div> <div class="right-menu"> <!-- æ¶æ¯éç¥ --> <el-popover @@ -87,14 +70,10 @@ import useAppStore from '@/store/modules/app' import useUserStore from '@/store/modules/user' import useSettingsStore from '@/store/modules/settings' import { userLoginFacotryList } from "@/api/system/user.js" import Cookies from "js-cookie"; import { decrypt } from "@/utils/jsencrypt" const appStore = useAppStore() const userStore = useUserStore() const settingsStore = useSettingsStore() const factoryList = ref([]) const notificationVisible = ref(false) const notificationCenterRef = ref(null) const unreadCount = ref(0) @@ -139,42 +118,6 @@ function toggleTheme() { settingsStore.toggleTheme() } function getUserLoginFacotryList() { if (userStore.id) { userLoginFacotryList({ userId: userStore.id }).then(res => { console.log('res', res) factoryList.value = res.data }) } else { factoryList.value = [] } } function handleFactoryChange(command) { console.log('command', command) handleLogin(command.deptId); } function handleLogin(currentFatoryId) { const loginForm = { username: Cookies.get("username"), password: Cookies.get("password") === undefined ? null : decrypt(Cookies.get("password")), currentFatoryId: currentFatoryId } userStore.loginCheckFactory(loginForm).then(res => { forceReload(); }).catch((err) => { console.log(err) }) } function forceReload() { const currentUrl = window.location.origin + window.location.pathname; const timestamp = new Date().getTime(); window.location.href = `${currentUrl}?reload=${timestamp}`; } getUserLoginFacotryList(); // æ¶æ¯éç¥ç¸å ³ function handleUnreadCountChange(count) { @@ -221,22 +164,6 @@ position: relative; background: var(--navbar-bg); box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); .center-menu { line-height: 50px; position: absolute; left: 50%; transform: translateX(-50%); display: flex; align-items: center; .label { font-weight: bold; font-size: 18px; color: #333333; margin-right: 10px; } } .hamburger-container { line-height: 46px; src/store/modules/user.js
@@ -99,9 +99,8 @@ loginCheckFactory(userInfo) { const username = userInfo.username.trim() const password = userInfo.password const factoryId = userInfo.currentFatoryId return new Promise((resolve, reject) => { loginCheckFactory(username, password, factoryId).then(res => { loginCheckFactory(username, password).then(res => { setToken(res.token) this.token = res.token resolve() src/views/login.vue
@@ -10,7 +10,6 @@ size="large" auto-complete="off" placeholder="è´¦å·" @input="getUserLoginFacotryList" > <template #prefix><el-icon><User /></el-icon></template> </el-input> @@ -27,11 +26,6 @@ > <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template> </el-input> </el-form-item> <el-form-item prop="currentFatoryId"> <el-select v-model="loginForm.currentFatoryId" placeholder="è¯·éæ©å ¬å¸" > <el-option v-for="item in factoryList" :key="item.deptId" :label="item.deptName" :value="item.deptId" /> </el-select> </el-form-item> <!-- <el-form-item prop="code" v-if="captchaEnabled">--> <!-- <el-input--> @@ -77,7 +71,6 @@ import Cookies from "js-cookie" import { encrypt, decrypt } from "@/utils/jsencrypt" import useUserStore from '@/store/modules/user' import {userLoginFacotryList} from "@/api/system/user.js" const title = import.meta.env.VITE_APP_TITLE const userStore = useUserStore() @@ -89,7 +82,6 @@ username: "", password: "", rememberMe: false, currentFatoryId:'', }) const loginRules = { @@ -105,9 +97,6 @@ // 注åå¼å ³ const register = ref(false) const redirect = ref(undefined) const factoryList = ref([]) const currentFatoryId = ref('') watch(route, (newRoute) => { redirect.value = newRoute.query && newRoute.query.redirect @@ -162,20 +151,8 @@ } } function getUserLoginFacotryList() { if(loginForm.value.username){ userLoginFacotryList({userName:loginForm.value.username}).then(res => { console.log('res', res) factoryList.value = res.data }) }else { factoryList.value = [] } } getCode() getCookie() getUserLoginFacotryList() </script> <style lang='scss' scoped> src/views/procurementManagement/paymentLedger/index.vue
@@ -43,7 +43,7 @@ /> <el-table-column label="ä¾åºååç§°" prop="supplierName" /> <el-table-column label="å票éé¢(å )" label="ååéé¢(å )" prop="invoiceAmount" show-overflow-tooltip :formatter="formattedNumber" @@ -83,6 +83,7 @@ :column="tableColumnSon" :tableData="originalTableDataSon" :isSelection="false" :isShowPagination="false" :tableLoading="tableLoadingSon" :isShowSummary="isShowSummarySon" :summaryMethod="summarizeMainTable1" @@ -94,14 +95,6 @@ </el-text> </template> </PIMTable> <pagination v-show="sonTotal > 0" :total="sonTotal" @pagination="sonPaginationSearch" :layout="page.layout" :page="sonPage.current" :limit="sonPage.size" /> </div> </el-col> </el-row> @@ -117,25 +110,6 @@ } from "@/api/procurementManagement/paymentLedger.js"; import Pagination from "../../../components/PIMTable/Pagination.vue"; const tableColumn = ref([ { label: "ä¾åºååç§°", prop: "supplierName", width:240 }, { label: "å票éé¢(å )", prop: "invoiceAmount", }, { label: "仿¬¾éé¢(å )", prop: "paymentAmount", }, { label: "åºä»éé¢(å )", prop: "payableAmount", }, ]); const tableData = ref([]); const tableLoading = ref(false); const data = reactive({ @@ -164,11 +138,16 @@ const tableColumnSon = ref([ { label: "åçæ¥æ", prop: "happenTime", prop: "paymentDate", width: 110, }, { label: "å票éé¢(å )", label: "éè´ååå·", prop: "purchaseContractNumber", width: 150, }, { label: "ååéé¢(å )", prop: "invoiceAmount", width: 200, formatData: (params) => { @@ -177,7 +156,7 @@ }, { label: "仿¬¾éé¢(å )", prop: "currentPaymentAmount", prop: "paymentAmount", width: 200, formatData: (params) => { return params ? parseFloat(params).toFixed(2) : 0; @@ -214,7 +193,7 @@ const summarizeMainTable1 = (param) => { let summarizeTable = proxy.summarizeTable( param, ["invoiceAmount", "currentPaymentAmount"], ["invoiceAmount", "paymentAmount"], { ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° @@ -245,8 +224,6 @@ paymentLedgerList({ ...searchForm.value, ...page, detailPageNum: detailPageNum.value, // æ°å¢ detailPageSize: detailPageSize.value, // æ°å¢ }).then((res) => { let result = res.data; tableLoading.value = false; @@ -261,7 +238,7 @@ const getPaymenRecordtList = (supplierId) => { tableLoadingSon.value = true; paymentRecordList(supplierId) paymentRecordList({supplierId: supplierId}) .then((res) => { tableLoadingSon.value = false; tableDataSon.value = res.data; src/views/procurementManagement/procurementLedger/index.vue
@@ -52,7 +52,7 @@ show-summary :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)" height="calc(100vh - 22em)" :row-class-name="tableRowClassName" > <el-table-column align="center" type="selection" width="55" /> src/views/salesManagement/indicatorStats/index.vue
@@ -31,7 +31,7 @@ <el-icon :size="30" color="#e6a23c"><Van /></el-icon> </div> <div class="stat-content"> <div class="stat-value">{{ indicatorKpis.shipmentRate }}%</div> <div class="stat-value">{{ indicatorKpis.shipRate }}%</div> <div class="stat-label">åè´§ç</div> </div> </div> @@ -41,44 +41,27 @@ <!-- 维度çé --> <el-row :gutter="20" class="search-row"> <el-col :span="6"> <el-select v-model="indicatorFilter.product" placeholder="产å" clearable> <el-option label="å ¨é¨äº§å" value="" /> <el-option label="P.O 42.5æ®éç¡ é ¸çæ°´æ³¥" value="P.O 42.5æ®éç¡ é ¸çæ°´æ³¥" /> <el-option label="P.S 32.5ç¿æ¸£ç¡ é ¸çæ°´æ³¥" value="P.S 32.5ç¿æ¸£ç¡ é ¸çæ°´æ³¥" /> <el-option label="P.C 32.5å¤åç¡ é ¸çæ°´æ³¥" value="P.C 32.5å¤åç¡ é ¸çæ°´æ³¥" /> </el-select> <el-tree-select v-model="indicatorFilter.productCategory" placeholder="产åç±»å«" clearable check-strictly :data="productOptions" :render-after-expand="false" style="width: 100%" /> </el-col> <el-col :span="6"> <el-select v-model="indicatorFilter.customer" placeholder="客æ·" clearable> <el-option label="å ¨é¨å®¢æ·" value="" /> <el-option label="åä¸å»ºæéå¢" value="åä¸å»ºæéå¢" /> <el-option label="é¿æ±æ··ååå ¬å¸" value="é¿æ±æ··ååå ¬å¸" /> <el-option label="æµ¦æ±æ°´æ³¥å¶åå" value="æµ¦æ±æ°´æ³¥å¶åå" /> </el-select> </el-col> <el-col :span="6"> <el-select v-model="indicatorFilter.region" placeholder="åºå" clearable> <el-option label="å ¨é¨åºå" value="" /> <el-option label="åä¸å°åº" value="åä¸å°åº" /> <el-option label="ååå°åº" value="ååå°åº" /> <el-option label="ååå°åº" value="ååå°åº" /> <el-select v-model="indicatorFilter.customerName" placeholder="客æ·" clearable filterable> <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName" /> </el-select> </el-col> <el-col :span="6"> <el-date-picker v-model="indicatorFilter.dateRange" type="daterange" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" value-format="YYYY-MM-DD" style="width: 100%" /> </el-col> <el-col :span="24" style="text-align: right; margin-top: 10px;"> <el-col :span="6" style="text-align: right;"> <el-button type="primary" @click="applyIndicatorFilter">æ¥è¯¢</el-button> <el-button @click="resetIndicatorFilter">éç½®</el-button> <el-button @click="exportIndicatorTable">å¯¼åºæ¥è¡¨</el-button> <el-button @click="exportIndicatorChart">导åºå¾è¡¨</el-button> </el-col> </el-row> <!-- å¾è¡¨åº --> <div class="chart-container"> <div ref="indicatorChartRef" style="width: 100%; height: 360px;"></div> <div ref="indicatorChartRef" class="chart-wrapper"></div> </div> <!-- ä¸ç»©ç»è®¡ï¼å¢éç»´åº¦ï¼æ 个人å§åï¼ --> @@ -88,8 +71,8 @@ <el-table-column prop="salesAmount" label="éå®é¢"> <template #default="scope">Â¥{{ scope.row.salesAmount.toLocaleString() }}</template> </el-table-column> <el-table-column prop="shipmentRate" label="åè´§ç"> <template #default="scope">{{ scope.row.shipmentRate }}%</template> <el-table-column prop="shipRate" label="åè´§ç"> <template #default="scope">{{ scope.row.shipRate }}</template> </el-table-column> <el-table-column prop="attainment" label="ç®æ è¾¾æç"> <template #default="scope"> @@ -104,35 +87,209 @@ </template> <script setup> import { ref, reactive, onMounted, nextTick } from 'vue' import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue' import { Document, Van, Tickets } from '@element-plus/icons-vue' import * as echarts from 'echarts' import { getTotalStatistics, getStatisticsTable } from '@/api/salesManagement/indicatorStats' import { productTreeList } from '@/api/basicData/product.js' import { customerList } from '@/api/salesManagement/salesLedger.js' import { ElMessage } from 'element-plus' const indicatorKpis = reactive({ orderCount: 1280, salesAmount: 9650000, shipmentRate: 89.2 orderCount: 0, salesAmount: 0, shipRate: 0 }) // æ¯å¦å±ç¤ºéå®å¢éæç»è¡¨ï¼æéå¼å¯ const showTeamPerformance = ref(false) const loading = ref(false) const indicatorFilter = reactive({ product: '', customer: '', region: '', productCategory: '', customerName: '', dateRange: [] }) const indicatorChartRef = ref(null) let indicatorChart = null const productOptions = ref([]) const customerOption = ref([]) const teamPerformanceList = ref([ { team: 'åä¸å¤§åº', orderCount: 320, salesAmount: 2850000, shipmentRate: 90, attainment: 105 }, { team: 'åå大åº', orderCount: 280, salesAmount: 2150000, shipmentRate: 86, attainment: 92 }, { team: 'åå大åº', orderCount: 210, salesAmount: 1850000, shipmentRate: 88, attainment: 78 }, { team: '西å大åº', orderCount: 180, salesAmount: 1500000, shipmentRate: 83, attainment: 74 } { team: 'åä¸å¤§åº', orderCount: 320, salesAmount: 2850000, shipRate: 90, attainment: 105 }, { team: 'åå大åº', orderCount: 280, salesAmount: 2150000, shipRate: 86, attainment: 92 }, { team: 'åå大åº', orderCount: 210, salesAmount: 1850000, shipRate: 88, attainment: 78 }, { team: '西å大åº', orderCount: 180, salesAmount: 1500000, shipRate: 83, attainment: 74 } ]) // 转æ¢äº§åæ æ°æ®ï¼å° id æ¹ä¸º value function convertIdToValue(data) { return data.map((item) => { const { id, children, ...rest } = item const newItem = { ...rest, value: id, // å° id æ¹ä¸º value } if (children && children.length > 0) { newItem.children = convertIdToValue(children) } return newItem }) } // è·åäº§åæ æ°æ® const getProductOptions = () => { return productTreeList().then((res) => { productOptions.value = convertIdToValue(res) }).catch((error) => { console.error('è·åäº§åæ å¤±è´¥:', error) ElMessage.error('è·å产åç±»å«å¤±è´¥') }) } // è·å客æ·å表 const getCustomerList = () => { return customerList().then((res) => { customerOption.value = res || [] }).catch((error) => { console.error('è·å客æ·å表失败:', error) ElMessage.error('è·å客æ·å表失败') }) } // æ ¹æ® id æ¥æ¾äº§åç±»å«åç§° const findNodeLabelById = (nodes, id) => { if (!id) return null for (let i = 0; i < nodes.length; i++) { if (nodes[i].value === id) { return nodes[i].label } if (nodes[i].children && nodes[i].children.length > 0) { const found = findNodeLabelById(nodes[i].children, id) if (found) return found } } return null } // è·å头é¨ç»è®¡æ°æ® const fetchTotalStatistics = async () => { try { loading.value = true const params = {} if (indicatorFilter.customerName) { params.customerName = indicatorFilter.customerName } if (indicatorFilter.productCategory) { // æ ¹æ® id æ¥æ¾äº§åç±»å«åç§° const categoryName = findNodeLabelById(productOptions.value, indicatorFilter.productCategory) if (categoryName) { params.productCategory = categoryName } } if (indicatorFilter.dateRange && indicatorFilter.dateRange.length === 2) { params.entryDateStart = indicatorFilter.dateRange[0] params.entryDateEnd = indicatorFilter.dateRange[1] } const res = await getTotalStatistics(params) if (res && res.data) { indicatorKpis.orderCount = res.data.total || 0 indicatorKpis.salesAmount = res.data.contractAmountTotal || 0 // åè´§ç妿æ¥å£æ²¡æè¿åï¼ä¿æåå¼æè®¾ä¸º0 // indicatorKpis.shipRate = res.data.shipRate || 0 } } catch (error) { console.error('è·å头é¨ç»è®¡å¤±è´¥:', error) ElMessage.error('è·åç»è®¡æ°æ®å¤±è´¥') } finally { loading.value = false } } // è·åæ±ç¶å¾æ°æ® const fetchStatisticsTable = async () => { try { loading.value = true const params = {} if (indicatorFilter.customerName) { params.customerName = indicatorFilter.customerName } if (indicatorFilter.productCategory) { // æ ¹æ® id æ¥æ¾äº§åç±»å«åç§° const categoryName = findNodeLabelById(productOptions.value, indicatorFilter.productCategory) if (categoryName) { params.productCategory = categoryName } } if (indicatorFilter.dateRange && indicatorFilter.dateRange.length === 2) { params.entryDateStart = indicatorFilter.dateRange[0] params.entryDateEnd = indicatorFilter.dateRange[1] } const res = await getStatisticsTable(params) if (res && res.data) { updateChart(res.data) } } catch (error) { console.error('è·åå¾è¡¨æ°æ®å¤±è´¥:', error) ElMessage.error('è·åå¾è¡¨æ°æ®å¤±è´¥') } finally { loading.value = false } } // æ´æ°å¾è¡¨ const updateChart = (chartData) => { if (!indicatorChartRef.value) return if (indicatorChart) indicatorChart.dispose() indicatorChart = echarts.init(indicatorChartRef.value) // æ ¹æ®æ¥å£è¿åçæ°æ®ç»ææ´æ°å¾è¡¨ // æ¥å£è¿å: dateList, orderCountList, salesAmountList const option = { title: { text: 'å¤ç»´åº¦é宿æ è¶å¿', left: 'center' }, tooltip: { trigger: 'axis' }, legend: { data: ['è®¢åæ°', 'éå®é¢'], top: 30 }, grid: { left: '3%', right: '8%', bottom: '3%', containLabel: true }, xAxis: { type: 'category', data: chartData.dateList || [] }, yAxis: [ { type: 'value', name: 'éé¢', position: 'left', axisLabel: { formatter: '{value}' } }, { type: 'value', name: 'æ°é', position: 'right', minInterval: 1, axisLabel: { formatter: (value) => { const intValue = Math.round(value) return intValue.toString() } } } ], series: [ { name: 'è®¢åæ°', type: 'line', yAxisIndex: 1, data: chartData.orderCountList || [], itemStyle: { color: '#409eff' } }, { name: 'éå®é¢', type: 'bar', yAxisIndex: 0, data: chartData.salesAmountList || [], itemStyle: { color: '#67c23a' } } ] } indicatorChart.setOption(option) } const initIndicatorChart = () => { if (!indicatorChartRef.value) return @@ -141,75 +298,73 @@ const option = { title: { text: 'å¤ç»´åº¦é宿æ è¶å¿', left: 'center' }, tooltip: { trigger: 'axis' }, legend: { data: ['è®¢åæ°', 'éå®é¢', 'åè´§ç'], top: 30 }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis: { type: 'category', data: ['2024-12', '2025-01', '2025-02', '2025-03', '2025-04', '2025-05'] }, legend: { data: ['è®¢åæ°', 'éå®é¢'], top: 30 }, grid: { left: '3%', right: '8%', bottom: '3%', containLabel: true }, xAxis: { type: 'category', data: [] }, yAxis: [ { type: 'value', name: 'æ°é/éé¢', axisLabel: { formatter: '{value}' } }, { type: 'value', name: 'æ¯ä¾(%)', min: 0, max: 100, axisLabel: { formatter: '{value}%' } } { type: 'value', name: 'éé¢', position: 'left', axisLabel: { formatter: '{value}' } }, { type: 'value', name: 'æ°é', position: 'right', minInterval: 1, axisLabel: { formatter: (value) => { const intValue = Math.round(value) return intValue.toString() } } } ], series: [ { name: 'è®¢åæ°', type: 'bar', data: [180, 220, 210, 260, 205, 225], itemStyle: { color: '#409eff' } }, { name: 'éå®é¢', type: 'bar', data: [820, 950, 910, 1080, 980, 1020], itemStyle: { color: '#67c23a' } }, { name: 'åè´§ç', type: 'line', yAxisIndex: 1, data: [86, 89, 88, 91, 87, 90], itemStyle: { color: '#e6a23c' } } { name: 'è®¢åæ°', type: 'line', yAxisIndex: 1, data: [], itemStyle: { color: '#409eff' } }, { name: 'éå®é¢', type: 'bar', yAxisIndex: 0, data: [], itemStyle: { color: '#67c23a' } } ] } indicatorChart.setOption(option) } const applyIndicatorFilter = () => { const random = (base, delta) => { const v = base + Math.round((Math.random() - 0.5) * delta) return v < 0 ? 0 : v } indicatorKpis.orderCount = random(1280, 120) indicatorKpis.salesAmount = random(9650000, 350000) indicatorKpis.shipmentRate = (85 + Math.random() * 10).toFixed(1) * 1 setTimeout(() => initIndicatorChart(), 200) const applyIndicatorFilter = async () => { await Promise.all([ fetchTotalStatistics(), fetchStatisticsTable() ]) } const resetIndicatorFilter = () => { indicatorFilter.product = '' indicatorFilter.customer = '' indicatorFilter.region = '' indicatorFilter.productCategory = '' indicatorFilter.customerName = '' indicatorFilter.dateRange = [] applyIndicatorFilter() } const exportIndicatorTable = () => { const header = ['éå®å¢é', 'è®¢åæ°', 'éå®é¢', 'åè´§ç(%)', 'ç®æ è¾¾æç(%)'] const rows = teamPerformanceList.value.map(r => [ r.team, r.orderCount, r.salesAmount, r.shipmentRate, r.attainment ]) const csv = [header, ...rows].map(r => r.join(',')).join('\n') const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = 'ææ ç»è®¡-å¢éä¸ç»©.csv' document.body.appendChild(link) link.click() document.body.removeChild(link) URL.revokeObjectURL(url) // çªå£å¤§å°ååæ¶è°æ´å¾è¡¨å¤§å° const handleResize = () => { if (indicatorChart) { indicatorChart.resize() } const exportIndicatorChart = () => { if (!indicatorChart) return const url = indicatorChart.getDataURL({ type: 'png', pixelRatio: 2, backgroundColor: '#fff' }) const link = document.createElement('a') link.href = url link.download = 'ææ ç»è®¡-å¾è¡¨.png' document.body.appendChild(link) link.click() document.body.removeChild(link) } onMounted(() => { nextTick(() => initIndicatorChart()) nextTick(() => { initIndicatorChart() getProductOptions() getCustomerList() fetchTotalStatistics() fetchStatisticsTable() }) // çå¬çªå£å¤§å°åå window.addEventListener('resize', handleResize) }) onUnmounted(() => { // ç§»é¤çªå£å¤§å°ååçå¬å¨ window.removeEventListener('resize', handleResize) // 鿝å¾è¡¨å®ä¾ if (indicatorChart) { indicatorChart.dispose() indicatorChart = null } }) </script> @@ -225,7 +380,20 @@ .stat-content { flex: 1; } .stat-value { font-size: 28px; font-weight: bold; color: #303133; margin-bottom: 4px; } .stat-label { font-size: 14px; color: #909399; } .chart-container { margin: 20px 0; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .chart-container { margin: 20px 0; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); width: 100%; overflow: hidden; } .chart-wrapper { width: 100%; height: 360px; min-width: 0; } </style> src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -130,14 +130,6 @@ </template> </el-table-column> </el-table> <pagination v-show="recordTotal > 0" :total="recordTotal" layout="total, sizes, prev, pager, next, jumper" :page="recordPage.current" :limit="recordPage.size" @pagination="recordPaginationChange" /> </div> </div> </div> @@ -178,7 +170,6 @@ getList(); }; const paginationChange = (obj) => { console.log("paginationChange", current, limit); page.current = obj.page; page.size = obj.limit; getList(); src/views/salesManagement/salesLedger/index.vue
@@ -75,8 +75,8 @@ </template> </el-table-column> <el-table-column label="åè´§æ¥æ" prop="shippingDate" width="120" show-overflow-tooltip /> <el-table-column label="å½å ¥æ¥æ" prop="entryDate" width="120" show-overflow-tooltip /> <el-table-column label="ç¾è®¢æ¥æ" prop="executionDate" width="120" show-overflow-tooltip /> <el-table-column label="å½å ¥æ¥æ" prop="entryDate" width="120" show-overflow-tooltip /> <el-table-column fixed="right" label="æä½" min-width="200" align="center"> <template #default="scope"> <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">ç¼è¾</el-button> src/views/system/dept/index.vue
@@ -1,17 +1,17 @@ <template> <div class="app-container"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> <el-form-item label="å ¬å¸åç§°" prop="deptName"> <el-form-item label="é¨é¨åç§°" prop="deptName"> <el-input v-model="queryParams.deptName" placeholder="请è¾å ¥å ¬å¸åç§°" placeholder="请è¾å ¥é¨é¨åç§°" clearable style="width: 200px" @keyup.enter="handleQuery" /> </el-form-item> <el-form-item label="ç¶æ" prop="status"> <el-select v-model="queryParams.status" placeholder="å ¬å¸ç¶æ" clearable style="width: 200px"> <el-select v-model="queryParams.status" placeholder="é¨é¨ç¶æ" clearable style="width: 200px"> <el-option v-for="dict in sys_normal_disable" :key="dict.value" @@ -55,7 +55,7 @@ :default-expand-all="isExpandAll" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > <el-table-column prop="deptName" label="å ¬å¸åç§°" width="260"></el-table-column> <el-table-column prop="deptName" label="é¨é¨åç§°" width="260"></el-table-column> <el-table-column prop="orderNum" label="æåº" width="200"></el-table-column> <el-table-column prop="status" label="ç¶æ" width="100"> <template #default="scope"> @@ -76,25 +76,25 @@ </el-table-column> </el-table> <!-- æ·»å æä¿®æ¹å ¬å¸å¯¹è¯æ¡ --> <!-- æ·»å æä¿®æ¹é¨é¨å¯¹è¯æ¡ --> <el-dialog :title="title" v-model="open" width="600px" append-to-body> <el-form ref="deptRef" :model="form" :rules="rules" label-width="80px"> <el-row> <el-col :span="24" v-if="form.parentId !== 0"> <el-form-item label="ä¸çº§å ¬å¸" prop="parentId"> <el-form-item label="ä¸çº§é¨é¨" prop="parentId"> <el-tree-select v-model="form.parentId" :data="deptOptions" :props="{ value: 'deptId', label: 'deptName', children: 'children' }" value-key="deptId" placeholder="éæ©ä¸çº§å ¬å¸" placeholder="éæ©ä¸çº§é¨é¨" check-strictly /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å ¬å¸åç§°" prop="deptName"> <el-input v-model="form.deptName" placeholder="请è¾å ¥å ¬å¸åç§°" /> <el-form-item label="é¨é¨åç§°" prop="deptName"> <el-input v-model="form.deptName" placeholder="请è¾å ¥é¨é¨åç§°" /> </el-form-item> </el-col> <el-col :span="12"> @@ -118,7 +118,7 @@ </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å ¬å¸ç¶æ"> <el-form-item label="é¨é¨ç¶æ"> <el-radio-group v-model="form.status"> <el-radio v-for="dict in sys_normal_disable" @@ -129,8 +129,8 @@ </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å ¬å¸ç¼å·" prop="deptNick"> <el-input v-model="form.deptNick" placeholder="请è¾å ¥å ¬å¸ç¼å·" maxlength="50" /> <el-form-item label="é¨é¨ç¼å·" prop="deptNick"> <el-input v-model="form.deptNick" placeholder="请è¾å ¥é¨é¨ç¼å·" maxlength="50" /> </el-form-item> </el-col> </el-row> @@ -167,18 +167,18 @@ status: undefined }, rules: { parentId: [{ required: true, message: "ä¸çº§å ¬å¸ä¸è½ä¸ºç©º", trigger: "blur" }], deptName: [{ required: true, message: "å ¬å¸åç§°ä¸è½ä¸ºç©º", trigger: "blur" }], parentId: [{ required: true, message: "ä¸çº§é¨é¨ä¸è½ä¸ºç©º", trigger: "blur" }], deptName: [{ required: true, message: "é¨é¨åç§°ä¸è½ä¸ºç©º", trigger: "blur" }], orderNum: [{ required: true, message: "æ¾ç¤ºæåºä¸è½ä¸ºç©º", trigger: "blur" }], email: [{ type: "email", message: "请è¾å ¥æ£ç¡®çé®ç®±å°å", trigger: ["blur", "change"] }], phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请è¾å ¥æ£ç¡®çææºå·ç ", trigger: "blur" }], deptNick: [{ required: true, message: "å ¬å¸ç¼å·ä¸è½ä¸ºç©º", trigger: "blur" }], deptNick: [{ required: true, message: "é¨é¨ç¼å·ä¸è½ä¸ºç©º", trigger: "blur" }], }, }) const { queryParams, form, rules } = toRefs(data) /** æ¥è¯¢å ¬å¸å表 */ /** æ¥è¯¢é¨é¨å表 */ function getList() { loading.value = true listDept(queryParams.value).then(response => { @@ -230,7 +230,7 @@ form.value.parentId = row.deptId } open.value = true title.value = "æ·»å å ¬å¸" title.value = "æ·»å é¨é¨" } /** å±å¼/æå æä½ */ @@ -251,7 +251,7 @@ getDept(row.deptId).then(response => { form.value = response.data open.value = true title.value = "ä¿®æ¹å ¬å¸" title.value = "ä¿®æ¹é¨é¨" }) } src/views/system/user/index.vue
@@ -1,209 +1,69 @@ <template> <div class="app-container"> <el-row :gutter="20" style="height: calc(100vh - 8em)"> <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme" > <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme"> <!--é¨é¨æ°æ®--> <pane size="16"> <el-col style="padding: 10px"> <div class="head-container"> <el-input v-model="deptName" placeholder="请è¾å ¥é¨é¨åç§°" clearable prefix-icon="Search" style="margin-bottom: 20px" /> <el-input v-model="deptName" placeholder="请è¾å ¥é¨é¨åç§°" clearable prefix-icon="Search" style="margin-bottom: 20px" /> </div> <div class="head-container"> <el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" /> <el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" /> </div> </el-col> </pane> <!--ç¨æ·æ°æ®--> <pane size="84"> <el-col style="padding: 10px"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px" > <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> <el-form-item label="ç»å½è´¦å·" prop="userName"> <el-input v-model="queryParams.userName" placeholder="请è¾å ¥ç»å½è´¦å·" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.userName" placeholder="请è¾å ¥ç»å½è´¦å·" clearable style="width: 240px" @keyup.enter="handleQuery" /> </el-form-item> <el-form-item label="ææºå·ç " prop="phonenumber"> <el-input v-model="queryParams.phonenumber" placeholder="请è¾å ¥ææºå·ç " clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.phonenumber" placeholder="请è¾å ¥ææºå·ç " clearable style="width: 240px" @keyup.enter="handleQuery" /> </el-form-item> <el-form-item label="ç¶æ" prop="status"> <el-select v-model="queryParams.status" placeholder="ç¨æ·ç¶æ" clearable style="width: 240px" > <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="ç¨æ·ç¶æ" clearable style="width: 240px"> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="å建æ¶é´" style="width: 308px"> <el-date-picker v-model="dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" ></el-date-picker> <el-date-picker v-model="dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ"></el-date-picker> </el-form-item> <el-form-item> <el-button type="primary" icon="Search" @click="handleQuery" >æç´¢</el-button > <el-button type="primary" icon="Search" @click="handleQuery">æç´¢</el-button> <el-button icon="Refresh" @click="resetQuery">éç½®</el-button> </el-form-item> </el-form> <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:user:add']" >æ°å¢</el-button > <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:user:add']">æ°å¢</el-button> </el-col> <el-col :span="1.5"> <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['system:user:edit']" >ä¿®æ¹</el-button > <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['system:user:edit']">ä¿®æ¹</el-button> </el-col> <el-col :span="1.5"> <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:user:remove']" >å é¤</el-button > <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:user:remove']">å é¤</el-button> </el-col> <el-col :span="1.5"> <el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:user:import']" >å¯¼å ¥</el-button > <el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:user:import']">å¯¼å ¥</el-button> </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:user:export']" >导åº</el-button > <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:user:export']">导åº</el-button> </el-col> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns" ></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar> </el-row> <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange" > <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="50" align="center" /> <el-table-column label="ç¨æ·ç¼å·" align="center" key="userId" prop="userId" v-if="columns[0].visible" /> <el-table-column label="ç»å½è´¦å·" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" /> <el-table-column label="ç¨æ·æµç§°" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" /> <el-table-column label="é¨é¨" align="center" key="deptNames" prop="deptNames" v-if="columns[3].visible" :show-overflow-tooltip="true" /> <el-table-column label="ææºå·ç " align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" /> <el-table-column label="ç¶æ" align="center" key="status" v-if="columns[5].visible" > <el-table-column label="ç¨æ·ç¼å·" align="center" key="userId" prop="userId" v-if="columns[0].visible" /> <el-table-column label="ç»å½è´¦å·" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" /> <el-table-column label="ç¨æ·æµç§°" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" /> <el-table-column label="é¨é¨" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" /> <el-table-column label="ææºå·ç " align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" /> <el-table-column label="ç¶æ" align="center" key="status" v-if="columns[5].visible"> <template #default="scope"> <el-switch v-model="scope.row.status" @@ -213,86 +73,29 @@ ></el-switch> </template> </el-table-column> <el-table-column label="å建æ¶é´" align="center" prop="createTime" v-if="columns[6].visible" width="160" > <el-table-column label="å建æ¶é´" align="center" prop="createTime" v-if="columns[6].visible" width="160"> <template #default="scope"> <span>{{ parseTime(scope.row.createTime) }}</span> </template> </el-table-column> <el-table-column label="æä½" align="center" width="150" class-name="small-padding fixed-width" > <el-table-column label="æä½" align="center" width="150" class-name="small-padding fixed-width"> <template #default="scope"> <el-tooltip content="ä¿®æ¹" placement="top" v-if="scope.row.userId !== 1" > <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']" ></el-button> <el-tooltip content="ä¿®æ¹" placement="top" v-if="scope.row.userId !== 1"> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button> </el-tooltip> <el-tooltip content="å é¤" placement="top" v-if="scope.row.userId !== 1" > <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']" ></el-button> <el-tooltip content="å é¤" placement="top" v-if="scope.row.userId !== 1"> <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button> </el-tooltip> <el-tooltip content="éç½®å¯ç " placement="top" v-if="scope.row.userId !== 1" > <el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']" ></el-button> <el-tooltip content="éç½®å¯ç " placement="top" v-if="scope.row.userId !== 1"> <el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button> </el-tooltip> <el-tooltip content="åé è§è²" placement="top" v-if="scope.row.userId !== 1" > <el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']" ></el-button> <el-tooltip content="åé è§è²" placement="top" v-if="scope.row.userId !== 1"> <el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button> </el-tooltip> </template> </el-table-column> </el-table> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> </el-col> </pane> </splitpanes> @@ -303,74 +106,53 @@ <el-form :model="form" :rules="rules" ref="userRef" label-width="80px"> <el-row> <el-col :span="12"> <el-form-item label="ç¨æ·æµç§°" prop="nickName"> <el-input v-model="form.nickName" placeholder="请è¾å ¥ç¨æ·æµç§°" maxlength="30" /> <el-form-item v-if="form.userId == undefined" label="ç»å½è´¦å·" prop="userName"> <el-input v-model="form.userName" placeholder="请è¾å ¥ç¨æ·åç§°" maxlength="30" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å½å±å ¬å¸" prop="deptIds"> <el-tree-select v-model="form.deptIds" :data="enabledDeptOptions" :render-after-expand="false" show-checkbox multiple placeholder="è¯·éæ©å½å±å ¬å¸" /> <el-form-item v-if="form.userId == undefined" label="ç¨æ·å¯ç " prop="password"> <el-input v-model="form.password" placeholder="请è¾å ¥ç¨æ·å¯ç " type="password" maxlength="20" show-password /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="ç¨æ·æµç§°" prop="nickName"> <el-input v-model="form.nickName" placeholder="请è¾å ¥ç¨æ·æµç§°" maxlength="30" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å½å±é¨é¨" prop="deptId"> <el-tree-select v-model="form.deptId" :data="enabledDeptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="è¯·éæ©å½å±é¨é¨" check-strictly /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="å²ä½" prop="postIds"> <el-select v-model="form.postIds" multiple placeholder="è¯·éæ©"> <el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId" :disabled="item.status == 1"></el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="è§è²" prop="roleIds"> <el-select v-model="form.roleIds" multiple placeholder="è¯·éæ©"> <el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId" :disabled="item.status == 1"></el-option> </el-select> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="ææºå·ç " prop="phonenumber"> <el-input v-model="form.phonenumber" placeholder="请è¾å ¥ææºå·ç " maxlength="11" /> <el-input v-model="form.phonenumber" placeholder="请è¾å ¥ææºå·ç " maxlength="11" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="é®ç®±" prop="email"> <el-input v-model="form.email" placeholder="请è¾å ¥é®ç®±" maxlength="50" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item v-if="form.userId == undefined" label="ç»å½è´¦å·" prop="userName" > <el-input v-model="form.userName" placeholder="请è¾å ¥ç»å½è´¦å·" maxlength="30" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item v-if="form.userId == undefined" label="ç¨æ·å¯ç " prop="password" > <el-input v-model="form.password" placeholder="请è¾å ¥ç¨æ·å¯ç " type="password" maxlength="20" show-password /> <el-input v-model="form.email" placeholder="请è¾å ¥é®ç®±" maxlength="50" /> </el-form-item> </el-col> </el-row> @@ -378,64 +160,22 @@ <el-col :span="12"> <el-form-item label="ç¨æ·æ§å«"> <el-select v-model="form.sex" placeholder="è¯·éæ©"> <el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value" ></el-option> <el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ç¶æ"> <el-radio-group v-model="form.status"> <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value" >{{ dict.label }}</el-radio > <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio> </el-radio-group> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="å²ä½"> <el-select v-model="form.postIds" multiple placeholder="è¯·éæ©"> <el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId" :disabled="item.status == 1" ></el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="è§è²" prop="roleIds"> <el-select v-model="form.roleIds" multiple placeholder="è¯·éæ©"> <el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId" :disabled="item.status == 1" ></el-option> </el-select> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="夿³¨"> <el-input v-model="form.remark" type="textarea" placeholder="请è¾å ¥å 容" ></el-input> <el-input v-model="form.remark" type="textarea" placeholder="请è¾å ¥å 容"></el-input> </el-form-item> </el-col> </el-row> @@ -449,41 +189,17 @@ </el-dialog> <!-- ç¨æ·å¯¼å ¥å¯¹è¯æ¡ --> <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body > <el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag > <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body> <el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag> <el-icon class="el-icon--upload"><upload-filled /></el-icon> <div class="el-upload__text">å°æä»¶æå°æ¤å¤ï¼æ<em>ç¹å»ä¸ä¼ </em></div> <template #tip> <div class="el-upload__tip text-center"> <div class="el-upload__tip"> <el-checkbox v-model="upload.updateSupport" />æ¯å¦æ´æ°å·²ç»åå¨çç¨æ·æ°æ® <el-checkbox v-model="upload.updateSupport" />æ¯å¦æ´æ°å·²ç»åå¨çç¨æ·æ°æ® </div> <span>ä» å è®¸å¯¼å ¥xlsãxlsxæ ¼å¼æä»¶ã</span> <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate" >ä¸è½½æ¨¡æ¿</el-link > <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">ä¸è½½æ¨¡æ¿</el-link> </div> </template> </el-upload> @@ -498,46 +214,33 @@ </template> <script setup name="User"> import { getToken } from "@/utils/auth"; import useAppStore from "@/store/modules/app"; import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser, deptTreeSelect, } from "@/api/system/user"; import { Splitpanes, Pane } from "splitpanes"; import "splitpanes/dist/splitpanes.css"; import {onMounted} from "vue"; import { getToken } from "@/utils/auth" import useAppStore from '@/store/modules/app' import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser, deptTreeSelect } from "@/api/system/user" import { Splitpanes, Pane } from "splitpanes" import "splitpanes/dist/splitpanes.css" const router = useRouter(); const appStore = useAppStore(); const { proxy } = getCurrentInstance(); const { sys_normal_disable, sys_user_sex } = proxy.useDict( "sys_normal_disable", "sys_user_sex" ); const router = useRouter() const appStore = useAppStore() const { proxy } = getCurrentInstance() const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex") const userList = ref([]); const open = ref(false); const loading = ref(true); const showSearch = ref(true); const ids = ref([]); const single = ref(true); const multiple = ref(true); const total = ref(0); const title = ref(""); const dateRange = ref([]); const deptName = ref(""); const deptOptions = ref(undefined); const enabledDeptOptions = ref(undefined); const initPassword = ref(undefined); const postOptions = ref([]); const roleOptions = ref([]); const userList = ref([]) const open = ref(false) const loading = ref(true) const showSearch = ref(true) const ids = ref([]) const single = ref(true) const multiple = ref(true) const total = ref(0) const title = ref("") const dateRange = ref([]) const deptName = ref("") const deptOptions = ref(undefined) const enabledDeptOptions = ref(undefined) const initPassword = ref(undefined) const postOptions = ref([]) const roleOptions = ref([]) /*** ç¨æ·å¯¼å ¥åæ° */ const upload = reactive({ // æ¯å¦æ¾ç¤ºå¼¹åºå±ï¼ç¨æ·å¯¼å ¥ï¼ @@ -551,8 +254,8 @@ // 设置ä¸ä¼ ç请æ±å¤´é¨ headers: { Authorization: "Bearer " + getToken() }, // ä¸ä¼ çå°å url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData", }); url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData" }) // åæ¾éä¿¡æ¯ const columns = ref([ { key: 0, label: `ç¨æ·ç¼å·`, visible: true }, @@ -561,8 +264,8 @@ { key: 3, label: `é¨é¨`, visible: true }, { key: 4, label: `ææºå·ç `, visible: true }, { key: 5, label: `ç¶æ`, visible: true }, { key: 6, label: `å建æ¶é´`, visible: true }, ]); { key: 6, label: `å建æ¶é´`, visible: true } ]) const data = reactive({ form: {}, @@ -572,189 +275,138 @@ userName: undefined, phonenumber: undefined, status: undefined, deptId: undefined, deptId: undefined }, rules: { userName: [ { required: true, message: "ç»å½è´¦å·ä¸è½ä¸ºç©º", trigger: "blur" }, { min: 2, max: 20, message: "ç»å½è´¦å·é¿åº¦å¿ é¡»ä»äº 2 å 20 ä¹é´", trigger: "blur", }, ], nickName: [ { required: true, message: "ç¨æ·æµç§°ä¸è½ä¸ºç©º", trigger: "blur" }, ], deptIds: [{ required: true, message: "å ¬å¸ä¸è½ä¸ºç©º", trigger: "change" }], roleIds: [{ required: true, message: "è§è²ä¸è½ä¸ºç©º", trigger: "change" }], password: [ { required: true, message: "ç¨æ·å¯ç ä¸è½ä¸ºç©º", trigger: "blur" }, { min: 5, max: 20, message: "ç¨æ·å¯ç é¿åº¦å¿ é¡»ä»äº 5 å 20 ä¹é´", trigger: "blur", }, { pattern: /^[^<>"'|\\]+$/, message: "ä¸è½å å«éæ³å符ï¼< > \" ' \\\ |", trigger: "blur", }, ], email: [ { type: "email", message: "请è¾å ¥æ£ç¡®çé®ç®±å°å", trigger: ["blur", "change"], }, ], phonenumber: [ { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请è¾å ¥æ£ç¡®çææºå·ç ", trigger: "blur", }, ], }, }); userName: [{ required: true, message: "ç¨æ·åç§°ä¸è½ä¸ºç©º", trigger: "blur" }, { min: 2, max: 20, message: "ç¨æ·åç§°é¿åº¦å¿ é¡»ä»äº 2 å 20 ä¹é´", trigger: "blur" }], nickName: [{ required: true, message: "ç¨æ·æµç§°ä¸è½ä¸ºç©º", trigger: "blur" }], password: [{ required: true, message: "ç¨æ·å¯ç ä¸è½ä¸ºç©º", trigger: "blur" }, { min: 5, max: 20, message: "ç¨æ·å¯ç é¿åº¦å¿ é¡»ä»äº 5 å 20 ä¹é´", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "ä¸è½å å«éæ³å符ï¼< > \" ' \\\ |", trigger: "blur" }], email: [{ type: "email", message: "请è¾å ¥æ£ç¡®çé®ç®±å°å", trigger: ["blur", "change"] }], phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请è¾å ¥æ£ç¡®çææºå·ç ", trigger: "blur" }], deptId: [{ required: true, message: "å½å±é¨é¨ä¸è½ä¸ºç©º", trigger: "change" }], postIds: [{ required: true, message: "å²ä½ä¸è½ä¸ºç©º", trigger: "change" }], roleIds: [{ required: true, message: "è§è²ä¸è½ä¸ºç©º", trigger: "change" }] } }) const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data) /** éè¿æ¡ä»¶è¿æ»¤èç¹ */ const filterNode = (value, data) => { if (!value) return true; return data.label.indexOf(value) !== -1; }; if (!value) return true return data.label.indexOf(value) !== -1 } /** æ ¹æ®åç§°çéé¨é¨æ */ watch(deptName, (val) => { proxy.$refs["deptTreeRef"].filter(val); }); watch(deptName, val => { proxy.$refs["deptTreeRef"].filter(val) }) /** æ¥è¯¢ç¨æ·å表 */ function getList() { loading.value = true; listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then( (res) => { loading.value = false; userList.value = res.rows; total.value = res.total; } ); loading.value = true listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => { loading.value = false userList.value = res.rows total.value = res.total }) } /** æ¥è¯¢é¨é¨ä¸ææ ç»æ */ function getDeptTree() { deptTreeSelect().then((response) => { deptOptions.value = response.data; enabledDeptOptions.value = filterDisabledDept( JSON.parse(JSON.stringify(response.data)) ); }); deptTreeSelect().then(response => { deptOptions.value = response.data enabledDeptOptions.value = filterDisabledDept(JSON.parse(JSON.stringify(response.data))) }) } /** è¿æ»¤ç¦ç¨çé¨é¨ */ function filterDisabledDept(deptList) { return deptList.filter((dept) => { return deptList.filter(dept => { if (dept.disabled) { return false; return false } if (dept.children && dept.children.length) { dept.children = filterDisabledDept(dept.children); dept.children = filterDisabledDept(dept.children) } return true; }); return true }) } /** èç¹åå»äºä»¶ */ function handleNodeClick(data) { queryParams.value.deptId = data.id; handleQuery(); queryParams.value.deptId = data.id handleQuery() } /** æç´¢æé®æä½ */ function handleQuery() { queryParams.value.pageNum = 1; getList(); queryParams.value.pageNum = 1 getList() } /** éç½®æé®æä½ */ function resetQuery() { dateRange.value = []; proxy.resetForm("queryRef"); queryParams.value.deptId = undefined; proxy.$refs.deptTreeRef.setCurrentKey(null); handleQuery(); dateRange.value = [] proxy.resetForm("queryRef") queryParams.value.deptId = undefined proxy.$refs.deptTreeRef.setCurrentKey(null) handleQuery() } /** å é¤æé®æä½ */ function handleDelete(row) { const userIds = row.userId || ids.value; proxy.$modal .confirm('æ¯å¦ç¡®è®¤å é¤ç¨æ·ç¼å·ä¸º"' + userIds + '"çæ°æ®é¡¹ï¼') .then(function () { return delUser(userIds); }) .then(() => { getList(); proxy.$modal.msgSuccess("å 餿å"); }) .catch(() => {}); const userIds = row.userId || ids.value proxy.$modal.confirm('æ¯å¦ç¡®è®¤å é¤ç¨æ·ç¼å·ä¸º"' + userIds + '"çæ°æ®é¡¹ï¼').then(function () { return delUser(userIds) }).then(() => { getList() proxy.$modal.msgSuccess("å 餿å") }).catch(() => {}) } /** å¯¼åºæé®æä½ */ function handleExport() { proxy.download( "system/user/export", { proxy.download("system/user/export", { ...queryParams.value, }, `user_${new Date().getTime()}.xlsx` ); },`user_${new Date().getTime()}.xlsx`) } /** ç¨æ·ç¶æä¿®æ¹ */ function handleStatusChange(row) { let text = row.status === "0" ? "å¯ç¨" : "åç¨"; proxy.$modal .confirm('确认è¦"' + text + '""' + row.userName + '"ç¨æ·å?') .then(function () { return changeUserStatus(row.userId, row.status); let text = row.status === "0" ? "å¯ç¨" : "åç¨" proxy.$modal.confirm('确认è¦"' + text + '""' + row.userName + '"ç¨æ·å?').then(function () { return changeUserStatus(row.userId, row.status) }).then(() => { proxy.$modal.msgSuccess(text + "æå") }).catch(function () { row.status = row.status === "0" ? "1" : "0" }) .then(() => { proxy.$modal.msgSuccess(text + "æå"); }) .catch(function () { row.status = row.status === "0" ? "1" : "0"; }); } /** æ´å¤æä½ */ function handleCommand(command, row) { switch (command) { case "handleResetPwd": handleResetPwd(row); break; handleResetPwd(row) break case "handleAuthRole": handleAuthRole(row); break; handleAuthRole(row) break default: break; break } } /** 跳转è§è²åé */ function handleAuthRole(row) { const userId = row.userId; router.push("/system/user-auth/role/" + userId); const userId = row.userId router.push("/system/user-auth/role/" + userId) } /** éç½®å¯ç æé®æä½ */ function handleResetPwd(row) { proxy .$prompt('请è¾å ¥"' + row.userName + '"çæ°å¯ç ', "æç¤º", { proxy.$prompt('请è¾å ¥"' + row.userName + '"çæ°å¯ç ', "æç¤º", { confirmButtonText: "ç¡®å®", cancelButtonText: "åæ¶", closeOnClickModal: false, @@ -762,63 +414,52 @@ inputErrorMessage: "ç¨æ·å¯ç é¿åº¦å¿ é¡»ä»äº 5 å 20 ä¹é´", inputValidator: (value) => { if (/<|>|"|'|\||\\/.test(value)) { return "ä¸è½å å«éæ³å符ï¼< > \" ' \\\ |"; return "ä¸è½å å«éæ³å符ï¼< > \" ' \\\ |" } }, }).then(({ value }) => { resetUserPwd(row.userId, value).then(response => { proxy.$modal.msgSuccess("ä¿®æ¹æåï¼æ°å¯ç æ¯ï¼" + value) }) .then(({ value }) => { resetUserPwd(row.userId, value).then((response) => { proxy.$modal.msgSuccess("ä¿®æ¹æåï¼æ°å¯ç æ¯ï¼" + value); }); }) .catch(() => {}); }).catch(() => {}) } /** éæ©æ¡æ° */ function handleSelectionChange(selection) { ids.value = selection.map((item) => item.userId); single.value = selection.length != 1; multiple.value = !selection.length; ids.value = selection.map(item => item.userId) single.value = selection.length != 1 multiple.value = !selection.length } /** å¯¼å ¥æé®æä½ */ function handleImport() { upload.title = "ç¨æ·å¯¼å ¥"; upload.open = true; upload.title = "ç¨æ·å¯¼å ¥" upload.open = true } /** ä¸è½½æ¨¡æ¿æä½ */ function importTemplate() { proxy.download( "system/user/importTemplate", {}, `user_template_${new Date().getTime()}.xlsx` ); proxy.download("system/user/importTemplate", { }, `user_template_${new Date().getTime()}.xlsx`) } /**æä»¶ä¸ä¼ ä¸å¤ç */ const handleFileUploadProgress = (event, file, fileList) => { upload.isUploading = true; }; upload.isUploading = true } /** æä»¶ä¸ä¼ æåå¤ç */ const handleFileSuccess = (response, file, fileList) => { upload.open = false; upload.isUploading = false; proxy.$refs["uploadRef"].handleRemove(file); proxy.$alert( "<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "å¯¼å ¥ç»æ", { dangerouslyUseHTMLString: true } ); getList(); }; upload.open = false upload.isUploading = false proxy.$refs["uploadRef"].handleRemove(file) proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "å¯¼å ¥ç»æ", { dangerouslyUseHTMLString: true }) getList() } /** æäº¤ä¸ä¼ æä»¶ */ function submitFileForm() { proxy.$refs["uploadRef"].submit(); proxy.$refs["uploadRef"].submit() } /** éç½®æä½è¡¨å */ @@ -835,68 +476,66 @@ status: "0", remark: undefined, postIds: [], roleIds: [], }; proxy.resetForm("userRef"); roleIds: [] } proxy.resetForm("userRef") } /** åæ¶æé® */ function cancel() { open.value = false; reset(); open.value = false reset() } /** æ°å¢æé®æä½ */ function handleAdd() { reset(); getUser().then((response) => { postOptions.value = response.posts; roleOptions.value = response.roles; open.value = true; title.value = "æ·»å ç¨æ·"; form.value.password = initPassword.value; }); reset() getUser().then(response => { postOptions.value = response.posts roleOptions.value = response.roles open.value = true title.value = "æ·»å ç¨æ·" form.value.password = initPassword.value }) } /** ä¿®æ¹æé®æä½ */ function handleUpdate(row) { reset(); const userId = row.userId || ids.value; getUser(userId).then((response) => { form.value = response.data; postOptions.value = response.posts; roleOptions.value = response.roles; form.value.postIds = response.postIds; form.value.roleIds = response.roleIds; form.value.deptIds = response.deptIds; open.value = true; title.value = "ä¿®æ¹ç¨æ·"; form.password = ""; }); reset() const userId = row.userId || ids.value getUser(userId).then(response => { form.value = response.data postOptions.value = response.posts roleOptions.value = response.roles form.value.postIds = response.postIds form.value.roleIds = response.roleIds open.value = true title.value = "ä¿®æ¹ç¨æ·" form.password = "" }) } /** æäº¤æé® */ function submitForm() { proxy.$refs["userRef"].validate((valid) => { proxy.$refs["userRef"].validate(valid => { if (valid) { if (form.value.userId != undefined) { updateUser(form.value).then((response) => { proxy.$modal.msgSuccess("ä¿®æ¹æå"); open.value = false; getList(); }); updateUser(form.value).then(response => { proxy.$modal.msgSuccess("ä¿®æ¹æå") open.value = false getList() }) } else { addUser(form.value).then((response) => { proxy.$modal.msgSuccess("æ°å¢æå"); open.value = false; getList(); }); addUser(form.value).then(response => { proxy.$modal.msgSuccess("æ°å¢æå") open.value = false getList() }) } } }); }) } onMounted(() => { getDeptTree(); getList(); }); getDeptTree() getList() </script>