进销存-升级
1.供应商往来展示联调修改
2.指标统计页面展示联调修改
3.用户管理新增修改时字段修改
4.不需要多用户登录
已添加1个文件
已修改12个文件
2227 ■■■■■ 文件已修改
src/api/procurementManagement/paymentLedger.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/indicatorStats.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PIMTable/PIMTable.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Navbar.vue 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login.vue 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentLedger/index.vue 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/indicatorStats/index.vue 346 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPaymentLedger/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/dept/index.vue 452 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/index.vue 1225 ●●●●● 补丁 | 查看 | 原始文档 | 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 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)
// çª—口大小变化时调整图表大小
const handleResize = () => {
  if (indicatorChart) {
    indicatorChart.resize()
  }
}
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,148 +1,148 @@
<template>
   <div class="app-container">
      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
         <el-form-item label="公司名称" prop="deptName">
            <el-input
               v-model="queryParams.deptName"
               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-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>
            <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:dept:add']"
            >新增</el-button>
         </el-col>
         <el-col :span="1.5">
            <el-button
               type="info"
               plain
               icon="Sort"
               @click="toggleExpandAll"
            >展开/折叠</el-button>
         </el-col>
         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
      </el-row>
      <el-table
         v-if="refreshTable"
         v-loading="loading"
         :data="deptList"
         row-key="deptId"
         :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="orderNum" label="排序" width="200"></el-table-column>
         <el-table-column prop="status" label="状态" width="100">
            <template #default="scope">
               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
            </template>
         </el-table-column>
         <el-table-column label="创建时间" align="center" prop="createTime" width="200">
            <template #default="scope">
               <span>{{ parseTime(scope.row.createTime) }}</span>
            </template>
         </el-table-column>
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
            <template #default="scope">
               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']">修改</el-button>
               <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']">新增</el-button>
               <el-button v-if="scope.row.parentId != 0" link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dept:remove']">删除</el-button>
            </template>
         </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-tree-select
                        v-model="form.parentId"
                        :data="deptOptions"
                        :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
                        value-key="deptId"
                        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>
               </el-col>
               <el-col :span="12">
                  <el-form-item label="显示排序" prop="orderNum">
                     <el-input-number v-model="form.orderNum" controls-position="right" :min="0"/>
                  </el-form-item>
               </el-col>
               <el-col :span="12">
                  <el-form-item label="负责人" prop="leader">
                     <el-input v-model="form.leader" placeholder="请输入负责人" maxlength="20" />
                  </el-form-item>
               </el-col>
               <el-col :span="12">
                  <el-form-item label="联系电话" prop="phone">
                     <el-input v-model="form.phone" 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-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-group>
                  </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>
              </el-col>
            </el-row>
         </el-form>
         <template #footer>
            <div class="dialog-footer">
               <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
               <el-button @click="cancel">取 æ¶ˆ</el-button>
            </div>
         </template>
      </el-dialog>
   </div>
    <div class="app-container">
        <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
            <el-form-item label="部门名称" prop="deptName">
                <el-input
                    v-model="queryParams.deptName"
                    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-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>
                <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:dept:add']"
                >新增</el-button>
            </el-col>
            <el-col :span="1.5">
                <el-button
                    type="info"
                    plain
                    icon="Sort"
                    @click="toggleExpandAll"
                >展开/折叠</el-button>
            </el-col>
            <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
        </el-row>
        <el-table
            v-if="refreshTable"
            v-loading="loading"
            :data="deptList"
            row-key="deptId"
            :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="orderNum" label="排序" width="200"></el-table-column>
            <el-table-column prop="status" label="状态" width="100">
                <template #default="scope">
                    <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
                </template>
            </el-table-column>
            <el-table-column label="创建时间" align="center" prop="createTime" width="200">
                <template #default="scope">
                    <span>{{ parseTime(scope.row.createTime) }}</span>
                </template>
            </el-table-column>
            <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
                <template #default="scope">
                    <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']">修改</el-button>
                    <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']">新增</el-button>
                    <el-button v-if="scope.row.parentId != 0" link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dept:remove']">删除</el-button>
                </template>
            </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-tree-select
                                v-model="form.parentId"
                                :data="deptOptions"
                                :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
                                value-key="deptId"
                                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>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="显示排序" prop="orderNum">
                            <el-input-number v-model="form.orderNum" controls-position="right" :min="0"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="负责人" prop="leader">
                            <el-input v-model="form.leader" placeholder="请输入负责人" maxlength="20" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="联系电话" prop="phone">
                            <el-input v-model="form.phone" 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-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-group>
                        </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>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
                    <el-button @click="cancel">取 æ¶ˆ</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
<script setup name="Dept">
@@ -161,129 +161,129 @@
const refreshTable = ref(true)
const data = reactive({
  form: {},
  queryParams: {
    deptName: undefined,
    status: undefined
  },
  rules: {
    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" }],
  },
    form: {},
    queryParams: {
        deptName: undefined,
        status: undefined
    },
    rules: {
        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" }],
    },
})
const { queryParams, form, rules } = toRefs(data)
/** æŸ¥è¯¢å…¬å¸åˆ—表 */
/** æŸ¥è¯¢éƒ¨é—¨åˆ—表 */
function getList() {
  loading.value = true
  listDept(queryParams.value).then(response => {
    deptList.value = proxy.handleTree(response.data, "deptId")
    loading.value = false
  })
    loading.value = true
    listDept(queryParams.value).then(response => {
        deptList.value = proxy.handleTree(response.data, "deptId")
        loading.value = false
    })
}
/** å–消按钮 */
function cancel() {
  open.value = false
  reset()
    open.value = false
    reset()
}
/** è¡¨å•重置 */
function reset() {
  form.value = {
    deptId: undefined,
    parentId: undefined,
    deptName: undefined,
    orderNum: 0,
    leader: undefined,
    phone: undefined,
    email: undefined,
    status: "0",
    deptNick: undefined,
  }
  proxy.resetForm("deptRef")
    form.value = {
        deptId: undefined,
        parentId: undefined,
        deptName: undefined,
        orderNum: 0,
        leader: undefined,
        phone: undefined,
        email: undefined,
        status: "0",
        deptNick: undefined,
    }
    proxy.resetForm("deptRef")
}
/** æœç´¢æŒ‰é’®æ“ä½œ */
function handleQuery() {
  getList()
    getList()
}
/** é‡ç½®æŒ‰é’®æ“ä½œ */
function resetQuery() {
  proxy.resetForm("queryRef")
  handleQuery()
    proxy.resetForm("queryRef")
    handleQuery()
}
/** æ–°å¢žæŒ‰é’®æ“ä½œ */
function handleAdd(row) {
  reset()
  listDept().then(response => {
    deptOptions.value = proxy.handleTree(response.data, "deptId")
  })
  if (row != undefined) {
    form.value.parentId = row.deptId
  }
  open.value = true
  title.value = "添加公司"
    reset()
    listDept().then(response => {
        deptOptions.value = proxy.handleTree(response.data, "deptId")
    })
    if (row != undefined) {
        form.value.parentId = row.deptId
    }
    open.value = true
    title.value = "添加部门"
}
/** å±•å¼€/折叠操作 */
function toggleExpandAll() {
  refreshTable.value = false
  isExpandAll.value = !isExpandAll.value
  nextTick(() => {
    refreshTable.value = true
  })
    refreshTable.value = false
    isExpandAll.value = !isExpandAll.value
    nextTick(() => {
        refreshTable.value = true
    })
}
/** ä¿®æ”¹æŒ‰é’®æ“ä½œ */
function handleUpdate(row) {
  reset()
  listDeptExcludeChild(row.deptId).then(response => {
    deptOptions.value = proxy.handleTree(response.data, "deptId")
  })
  getDept(row.deptId).then(response => {
    form.value = response.data
    open.value = true
    title.value = "修改公司"
  })
    reset()
    listDeptExcludeChild(row.deptId).then(response => {
        deptOptions.value = proxy.handleTree(response.data, "deptId")
    })
    getDept(row.deptId).then(response => {
        form.value = response.data
        open.value = true
        title.value = "修改部门"
    })
}
/** æäº¤æŒ‰é’® */
function submitForm() {
  proxy.$refs["deptRef"].validate(valid => {
    if (valid) {
      if (form.value.deptId != undefined) {
        updateDept(form.value).then(response => {
          proxy.$modal.msgSuccess("修改成功")
          open.value = false
          getList()
        })
      } else {
        addDept(form.value).then(response => {
          proxy.$modal.msgSuccess("新增成功")
          open.value = false
          getList()
        })
      }
    }
  })
    proxy.$refs["deptRef"].validate(valid => {
        if (valid) {
            if (form.value.deptId != undefined) {
                updateDept(form.value).then(response => {
                    proxy.$modal.msgSuccess("修改成功")
                    open.value = false
                    getList()
                })
            } else {
                addDept(form.value).then(response => {
                    proxy.$modal.msgSuccess("新增成功")
                    open.value = false
                    getList()
                })
            }
        }
    })
}
/** åˆ é™¤æŒ‰é’®æ“ä½œ */
function handleDelete(row) {
  proxy.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?').then(function() {
    return delDept(row.deptId)
  }).then(() => {
    getList()
    proxy.$modal.msgSuccess("删除成功")
  }).catch(() => {})
    proxy.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?').then(function() {
        return delDept(row.deptId)
    }).then(() => {
        getList()
        proxy.$modal.msgSuccess("删除成功")
    }).catch(() => {})
}
getList()
src/views/system/user/index.vue
@@ -1,902 +1,541 @@
<template>
  <div class="app-container">
    <el-row :gutter="20" style="height: calc(100vh - 8em)">
      <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"
              />
            </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"
              />
            </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-item label="登录账号" prop="userName">
                <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-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>
              </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-form-item>
              <el-form-item>
                <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-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-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-col>
              <el-col :span="1.5">
                <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-col>
              <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-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"
              >
                <template #default="scope">
                  <el-switch
                    v-model="scope.row.status"
                    active-value="0"
                    inactive-value="1"
                    @change="handleStatusChange(scope.row)"
                  ></el-switch>
                </template>
              </el-table-column>
              <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"
              >
                <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>
                  <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>
                  <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"
            />
          </el-col>
        </pane>
      </splitpanes>
    </el-row>
    <!-- æ·»åŠ æˆ–ä¿®æ”¹ç”¨æˆ·é…ç½®å¯¹è¯æ¡† -->
    <el-dialog :title="title" v-model="open" width="600px" append-to-body>
      <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>
          </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>
          </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-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-form-item>
          </el-col>
        </el-row>
        <el-row>
          <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-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-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-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
          <el-button @click="cancel">取 æ¶ˆ</el-button>
        </div>
      </template>
    </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-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"
              />是否更新已经存在的用户数据
            </div>
            <span>仅允许导入xls、xlsx格式文件。</span>
            <el-link
              type="primary"
              :underline="false"
              style="font-size: 12px; vertical-align: baseline"
              @click="importTemplate"
              >下载模板</el-link
            >
          </div>
        </template>
      </el-upload>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
          <el-button @click="upload.open = false">取 æ¶ˆ</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
    <div class="app-container">
        <el-row :gutter="20" style="height: calc(100vh - 8em)">
            <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" />
                        </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" />
                        </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-item label="登录账号" prop="userName">
                                <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-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>
                            </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-form-item>
                            <el-form-item>
                                <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-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-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-col>
                            <el-col :span="1.5">
                                <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-col>
                            <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-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="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"
                                        active-value="0"
                                        inactive-value="1"
                                        @change="handleStatusChange(scope.row)"
                                    ></el-switch>
                                </template>
                            </el-table-column>
                            <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">
                                <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>
                                    <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>
                                    <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" />
                    </el-col>
                </pane>
            </splitpanes>
        </el-row>
        <!-- æ·»åŠ æˆ–ä¿®æ”¹ç”¨æˆ·é…ç½®å¯¹è¯æ¡† -->
        <el-dialog :title="title" v-model="open" width="600px" append-to-body>
            <el-form :model="form" :rules="rules" ref="userRef" label-width="80px">
                <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-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-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 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-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-group>
                        </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-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
                    <el-button @click="cancel">取 æ¶ˆ</el-button>
                </div>
            </template>
        </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-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" />是否更新已经存在的用户数据
                        </div>
                        <span>仅允许导入xls、xlsx格式文件。</span>
                        <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
                    </div>
                </template>
            </el-upload>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
                    <el-button @click="upload.open = false">取 æ¶ˆ</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</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({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(用户导入)
  open: false,
  // å¼¹å‡ºå±‚标题(用户导入)
  title: "",
  // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
  isUploading: false,
  // æ˜¯å¦æ›´æ–°å·²ç»å­˜åœ¨çš„用户数据
  updateSupport: 0,
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData",
});
    // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(用户导入)
    open: false,
    // å¼¹å‡ºå±‚标题(用户导入)
    title: "",
    // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
    isUploading: false,
    // æ˜¯å¦æ›´æ–°å·²ç»å­˜åœ¨çš„用户数据
    updateSupport: 0,
    // è®¾ç½®ä¸Šä¼ çš„请求头部
    headers: { Authorization: "Bearer " + getToken() },
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
})
// åˆ—显隐信息
const columns = ref([
  { key: 0, label: `用户编号`, visible: true },
  { key: 1, label: `登录账号`, visible: true },
  { key: 2, label: `用户昵称`, visible: true },
  { key: 3, label: `部门`, visible: true },
  { key: 4, label: `手机号码`, visible: true },
  { key: 5, label: `状态`, visible: true },
  { key: 6, label: `创建时间`, visible: true },
]);
    { key: 0, label: `用户编号`, visible: true },
    { key: 1, label: `登录账号`, visible: true },
    { key: 2, label: `用户昵称`, visible: true },
    { key: 3, label: `部门`, visible: true },
    { key: 4, label: `手机号码`, visible: true },
    { key: 5, label: `状态`, visible: true },
    { key: 6, label: `创建时间`, visible: true }
])
const data = reactive({
  form: {},
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    userName: undefined,
    phonenumber: undefined,
    status: 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",
      },
    ],
  },
});
    form: {},
    queryParams: {
        pageNum: 1,
        pageSize: 10,
        userName: undefined,
        phonenumber: undefined,
        status: 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" }],
        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) => {
    if (dept.disabled) {
      return false;
    }
    if (dept.children && dept.children.length) {
      dept.children = filterDisabledDept(dept.children);
    }
    return true;
  });
    return deptList.filter(dept => {
        if (dept.disabled) {
            return false
        }
        if (dept.children && dept.children.length) {
            dept.children = filterDisabledDept(dept.children)
        }
        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",
    {
      ...queryParams.value,
    },
    `user_${new Date().getTime()}.xlsx`
  );
    proxy.download("system/user/export", {
        ...queryParams.value,
    },`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);
    })
    .then(() => {
      proxy.$modal.msgSuccess(text + "成功");
    })
    .catch(function () {
      row.status = row.status === "0" ? "1" : "0";
    });
    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"
    })
}
/** æ›´å¤šæ“ä½œ */
function handleCommand(command, row) {
  switch (command) {
    case "handleResetPwd":
      handleResetPwd(row);
      break;
    case "handleAuthRole":
      handleAuthRole(row);
      break;
    default:
      break;
  }
    switch (command) {
        case "handleResetPwd":
            handleResetPwd(row)
            break
        case "handleAuthRole":
            handleAuthRole(row)
            break
        default:
            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 + '"的新密码', "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      closeOnClickModal: false,
      inputPattern: /^.{5,20}$/,
      inputErrorMessage: "用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´",
      inputValidator: (value) => {
        if (/<|>|"|'|\||\\/.test(value)) {
          return "不能包含非法字符:< > \" ' \\\ |";
        }
      },
    })
    .then(({ value }) => {
      resetUserPwd(row.userId, value).then((response) => {
        proxy.$modal.msgSuccess("修改成功,新密码是:" + value);
      });
    })
    .catch(() => {});
    proxy.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        closeOnClickModal: false,
        inputPattern: /^.{5,20}$/,
        inputErrorMessage: "用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´",
        inputValidator: (value) => {
            if (/<|>|"|'|\||\\/.test(value)) {
                return "不能包含非法字符:< > \" ' \\\ |"
            }
        },
    }).then(({ value }) => {
        resetUserPwd(row.userId, value).then(response => {
            proxy.$modal.msgSuccess("修改成功,新密码是:" + value)
        })
    }).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()
}
/** é‡ç½®æ“ä½œè¡¨å• */
function reset() {
  form.value = {
    userId: undefined,
    deptId: undefined,
    userName: undefined,
    nickName: undefined,
    password: undefined,
    phonenumber: undefined,
    email: undefined,
    sex: undefined,
    status: "0",
    remark: undefined,
    postIds: [],
    roleIds: [],
  };
  proxy.resetForm("userRef");
    form.value = {
        userId: undefined,
        deptId: undefined,
        userName: undefined,
        nickName: undefined,
        password: undefined,
        phonenumber: undefined,
        email: undefined,
        sex: undefined,
        status: "0",
        remark: undefined,
        postIds: [],
        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) => {
    if (valid) {
      if (form.value.userId != undefined) {
        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();
        });
      }
    }
  });
    proxy.$refs["userRef"].validate(valid => {
        if (valid) {
            if (form.value.userId != undefined) {
                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()
                })
            }
        }
    })
}
onMounted(() => {
    getDeptTree();
    getList();
});
getDeptTree()
getList()
</script>