Kaynağa Gözat

移动端问题修复

xukai 5 ay önce
ebeveyn
işleme
5ea7c7a724

+ 101 - 0
src/api/instr/finance/bill.ts

@@ -0,0 +1,101 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-07-19 13:42:40
+ * @LastEditors: wanglj
+ * @LastEditTime: 2023-08-25 14:09:08
+ * @Description: file content
+ * @FilePath: \labsop_meno\frontend\packages\vue-next-admin-sub\src\api\finance\bill.ts
+ */
+import request from '/@/utils/micro_request.js';
+
+/**
+ * @method addInstr 新增仪器
+ */
+const basePath = import.meta.env.VITE_FINANCE;
+
+export function useBillApi() {
+  return {
+    // 计费订单
+    // 创建
+    createFinOrder(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'CreateFinBillableOrder', query);
+    },
+    // 删除
+    deleteFinOrder(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'DeleteFinBillableOrderByIds', query);
+    },
+    // 列表
+    getFinOrderList(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'GetFinBillableOrderList', query);
+    },
+    // 列表(增加权限控制)
+    getFinOrderListByScoped(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'GetFinBillableOrderListScoped', query);
+    },
+    // 更新
+    updateFinOrder(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'UpdateFinBillableOrderById', query);
+    },
+    // 课题组负责人确认订单费用
+    confirmFinOrder(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'ConfirmFinBillableOrderByIds', query);
+    },
+    // 详情
+    getFinOrder(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'GetFinBillableOrderById', query);
+    },
+    // 导出
+    exportFinOrder(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'ExportFinBillableOrderList', query);
+    },
+    // 财务账单
+    // 创建
+    createFinBill(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'CreateFinStatementBill', query);
+    },
+    // 详情
+    getFinBill(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'GetFinStatementBillById', query);
+    },
+    // 课题负责人确认账单费用
+    confirmFinBill(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'ConfirmFinStatementBill', query);
+    },
+    // 财务管理员核销账单费用
+    verificationFinBill(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'VerificationFinStatementBill', query);
+    },
+    // 列表
+    getFinBillList(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'GetFinStatementBillList', query);
+    },
+    // 列表
+    exportFinBillList(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'ExportFinStatementBillList', query);
+    },
+    // 课题组负责人确认订单费用
+    confirmOrder(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'ConfirmFinBillableOrderByIds', query);
+    },
+    // 计费订单列表
+    getFinBillableOrderList(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'GetFinBillableOrderNoGrandList', query);
+    },
+    // 计费订单列表
+    getMyOrderTotal(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'GetMyOrderTotal', query);
+    },
+    // 计费订单列表导出
+    exportFinBillableOrderFeeTypeList(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'ExportFinBillableOrderFeeTypeList', query);
+    },
+    // 导出平台费用明细
+    exportPlatformFinOrder(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'ExportPlatformFinOrder', query);
+    },
+    // 平台费用统计
+    platformFinOrderStatistics(query?: object) {
+      return request.postRequest(basePath, 'FinanceOrder', 'PlatformFinOrderStatistics', query);
+    },
+  };
+}

+ 5 - 3
src/stores/userInfo.ts

@@ -37,7 +37,8 @@ export const useUserInfo = defineStore('userInfo', {
       userRoleNames: '',
       userType: '',
       creditScore: 0,
-      pgName: ''
+      pgName: '',
+      platformId: 0
     },
     openId: '',
     unionId: '',
@@ -64,7 +65,7 @@ export const useUserInfo = defineStore('userInfo', {
         // 模拟数据,请求接口时,记得删除多余代码及对应依赖的引入
         const [err, res]: ToResponse = await to(sysApi.getUserByUserName())
         if (err) return
-        const { userInfo, perms, roles, projectGroupRes } = res?.data
+        const { userInfo, perms, roles, projectGroupRes, platformId } = res?.data
         // 模拟数据
         let defaultRoles: Array<string> = []
         let defaultAuthBtnList: Array<string> = []
@@ -105,7 +106,8 @@ export const useUserInfo = defineStore('userInfo', {
           userType: userInfo.userType,
           creditScore: userInfo.creditScore,
           pgName: projectGroupRes ? projectGroupRes.pgName : '',
-          projectGroup: res?.data?.projectGroupRes
+          projectGroup: res?.data?.projectGroupRes,
+          platformId
         }
         resolve(userInfos)
       })

+ 748 - 607
src/view/entry/components/allocate.vue

@@ -1,96 +1,205 @@
 <template>
-	<div class="facilities-dialog-container">
-		<el-dialog :title="state.dialog.title" @close="onCancel" :close-on-click-modal="false" v-model="state.dialog.isShowDialog" width="90%">
-			<header class="mb8">
-				<span>资源分配</span>
-				<el-form inline size="mini" label-width="0">
-					<el-form-item label="资源组" label-width="60px">
-						<el-select v-model="groupId" placeholder="全部资源组" clearable style="width: 120px" @change="filterOutListByGroupId">
-							<el-option v-for="item in molecularGroupList" :key="item.id" :label="item.groupName" :value="item.id"></el-option>
-						</el-select>
-					</el-form-item>
-					<el-form-item class="ml20">
-						<el-radio-group v-model="resClass" size="small" @change="filterOutList">
-							<el-radio label="">全部</el-radio>
-							<el-radio v-for="item in cellType" :key="item.dictValue" :label="item.dictValue">{{ item.dictLabel }}</el-radio>
-						</el-radio-group>
-					</el-form-item>
-					<!-- <el-form-item class="ml12">
+  <div class="facilities-dialog-container">
+    <el-dialog
+      :title="state.dialog.title"
+      @close="onCancel"
+      :close-on-click-modal="false"
+      v-model="state.dialog.isShowDialog"
+      width="90%"
+    >
+      <header class="mb8">
+        <span>资源分配</span>
+        <el-form
+          inline
+          size="mini"
+          label-width="0"
+        >
+          <el-form-item
+            label="资源组"
+            label-width="60px"
+          >
+            <el-select
+              v-model="groupId"
+              placeholder="全部资源组"
+              clearable
+              style="width: 120px"
+              @change="filterOutListByGroupId"
+            >
+              <el-option
+                v-for="item in molecularGroupList"
+                :key="item.id"
+                :label="item.groupName"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item class="ml20">
+            <el-radio-group
+              v-model="resClass"
+              size="small"
+              @change="filterOutList"
+            >
+              <el-radio label="">全部</el-radio>
+              <el-radio
+                v-for="item in cellType"
+                :key="item.dictValue"
+                :label="item.dictValue"
+              >
+                {{ item.dictLabel }}
+              </el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <!-- <el-form-item class="ml12">
 						<el-button color="#2c78ff" @click="onSubmit('20')" size="default">分配</el-button>
 					</el-form-item> -->
-				</el-form>
-			</header>
-			<div class="table-container">
-				<div class="confirm-table">
-					<el-alert :title="`待分配队列(已确认${comfirmTotal}人)`" type="warning" :closable="false" class="mb8" />
-					<el-empty v-if="state.confirmList.length == 0" description="暂无队列数据"></el-empty>
-					<ul v-else>
-						<li v-for="(item, index) in state.confirmList" :key="index" :class="{ active: index === state.active }" @click="state.active = index">
-							<!-- <el-checkbox v-model="item.checked" class="mr8"></el-checkbox> -->
-							<div class="text">
-								<p>
-									分配房间: <span class="assignRoom">{{ item.resName }}</span>
-								</p>
-								<p class="mb4 mt4">
-									{{ item.memberName }}
-									【{{ formatDate(new Date(item.appointStartDate), 'YYYY-mm-dd') }}~{{ formatDate(new Date(item.appointEndDate), 'mm-dd') }}】
-								</p>
-								<p>【{{ getDictLabel(cellType, item.cellSourceType) }}】{{ formatDate(new Date(item.queueTime), 'YYYY-mm-dd HH:MM:SS') }}</p>
-							</div>
-							<!-- <el-icon v-if="item.checked">
+        </el-form>
+      </header>
+      <div class="table-container">
+        <div class="confirm-table">
+          <el-alert
+            :title="`待分配队列(已确认${comfirmTotal}人)`"
+            type="warning"
+            :closable="false"
+            class="mb8"
+          />
+          <el-empty
+            v-if="state.confirmList.length == 0"
+            description="暂无队列数据"
+          ></el-empty>
+          <ul v-else>
+            <li
+              v-for="(item, index) in state.confirmList"
+              :key="index"
+              :class="{ active: index === state.active }"
+              @click="state.active = index"
+            >
+              <!-- <el-checkbox v-model="item.checked" class="mr8"></el-checkbox> -->
+              <div class="text">
+                <p>
+                  分配房间:
+                  <span class="assignRoom">{{ item.resName }}</span>
+                </p>
+                <p class="mb4 mt4">
+                  {{ item.memberName }}
+                  【{{ formatDate(new Date(item.appointStartDate), 'YYYY-mm-dd') }}~{{
+                    formatDate(new Date(item.appointEndDate), 'mm-dd')
+                  }}】
+                </p>
+                <p>
+                  【{{ getDictLabel(cellType, item.cellSourceType) }}】{{
+                    formatDate(new Date(item.queueTime), 'YYYY-mm-dd HH:MM:SS')
+                  }}
+                </p>
+              </div>
+              <!-- <el-icon v-if="item.checked">
 								<ele-Select />
 							</el-icon> -->
-							<div class="btns">
-								<el-button v-if="item.resId" link type="primary" @click="cancelAllocate(item)">取消</el-button>
-								<el-button link type="primary" @click="onDetail(item)">查看</el-button>
-							</div>
-						</li>
-					</ul>
-				</div>
-				<div class="out-table mr12">
-					<el-alert :title="`资源列表(共${state.resourceList.length}个)`" type="warning" :closable="false" class="mb8" />
-					<div class="out-table-wrap">
-						<el-row :gutter="20" class="out-table-wrap-row">
-							<!-- 资源列表 -->
-							<el-col :span="24" class="resourceList-col">
-								<el-row :gutter="10" v-if="state.resourceList.length">
-									<el-col :span="8" v-for="item in state.resourceList" :key="item.id" class="mb8" @click="onSelectResource(item)">
-										<div class="items" :class="{ active: curSelectedResourceId === item.id }">
-											<header>
-												<div class="title">
-													<span class="mr8">{{ item.resName }}</span>
-													<!-- <el-checkbox v-model="item.checked" class="mr8"></el-checkbox> -->
-												</div>
-												<div class="btns">
-													<el-tag size="small">{{ getDictLabel(cellType, item.resClass) }}</el-tag>
-												</div>
-											</header>
-											<p>{{ item.resLocation }}</p>
-											<footer>
-												<p>{{ item.usingSource || 0 }}/{{ item.maxNum }}</p>
-												<ul>
-													<li v-for="(p, index) in item.usedList" :key="index" :class="getUsedClass(p)"></li>
-												</ul>
-											</footer>
-										</div>
-									</el-col>
-								</el-row>
-							</el-col>
-						</el-row>
-					</div>
-				</div>
-				<div class="use-table out-table">
-					<el-alert title="房间使用情况" type="warning" :closable="false" class="mb8" />
-					<div class="out-table-wrap">
-						<el-row :gutter="20" class="out-table-wrap-row">
-							<!-- 资源列表 -->
-							<el-col :span="24" class="allocation-situation-col">
-								<!-- 资源分配情况页面 -->
-								<div class="allocation-situation">
-									<ul class="position-choose">
-										<li v-for="(p, index) in allocationSituationList" :key="index" class="flex">
-											<i :class="getUsedClass(p)"></i>
-											<!-- <span v-if="p.userObj"
+              <div class="btns">
+                <el-button
+                  v-if="item.resId"
+                  link
+                  type="primary"
+                  @click="cancelAllocate(item)"
+                >
+                  取消
+                </el-button>
+                <el-button
+                  link
+                  type="primary"
+                  @click="onDetail(item)"
+                >
+                  查看
+                </el-button>
+              </div>
+            </li>
+          </ul>
+        </div>
+        <div class="out-table mr12">
+          <el-alert
+            :title="`资源列表(共${state.resourceList.length}个)`"
+            type="warning"
+            :closable="false"
+            class="mb8"
+          />
+          <div class="out-table-wrap">
+            <el-row
+              :gutter="20"
+              class="out-table-wrap-row"
+            >
+              <!-- 资源列表 -->
+              <el-col
+                :span="24"
+                class="resourceList-col"
+              >
+                <el-row
+                  :gutter="10"
+                  v-if="state.resourceList.length"
+                >
+                  <el-col
+                    :span="8"
+                    v-for="item in state.resourceList"
+                    :key="item.id"
+                    class="mb8"
+                    @click="onSelectResource(item)"
+                  >
+                    <div
+                      class="items"
+                      :class="{ active: curSelectedResourceId === item.id }"
+                    >
+                      <header>
+                        <div class="title">
+                          <span class="mr8">{{ item.resName }}</span>
+                          <!-- <el-checkbox v-model="item.checked" class="mr8"></el-checkbox> -->
+                        </div>
+                        <div class="btns">
+                          <el-tag size="small">{{ getDictLabel(cellType, item.resClass) }}</el-tag>
+                        </div>
+                      </header>
+                      <p>{{ item.resLocation }}</p>
+                      <footer>
+                        <p>{{ item.usingSource || 0 }}/{{ item.maxNum }}</p>
+                        <ul>
+                          <li
+                            v-for="(p, index) in item.usedList"
+                            :key="index"
+                            :class="getUsedClass(p)"
+                          ></li>
+                        </ul>
+                      </footer>
+                    </div>
+                  </el-col>
+                </el-row>
+              </el-col>
+            </el-row>
+          </div>
+        </div>
+        <div class="use-table out-table">
+          <el-alert
+            title="房间使用情况"
+            type="warning"
+            :closable="false"
+            class="mb8"
+          />
+          <div class="out-table-wrap">
+            <el-row
+              :gutter="20"
+              class="out-table-wrap-row"
+            >
+              <!-- 资源列表 -->
+              <el-col
+                :span="24"
+                class="allocation-situation-col"
+              >
+                <!-- 资源分配情况页面 -->
+                <div class="allocation-situation">
+                  <ul class="position-choose">
+                    <li
+                      v-for="(p, index) in allocationSituationList"
+                      :key="index"
+                      class="flex"
+                    >
+                      <i :class="getUsedClass(p)"></i>
+                      <!-- <span v-if="p.userObj"
 													>{{ p.userObj.memberName }}【{{ formatDate(new Date(new Date(p.userObj.appointStartDate)), 'YYYY-mm-dd') }}~{{
 														formatDate(new Date(new Date(p.userObj.appointEndDate)), 'mm-dd')
 													}}】</span
@@ -100,550 +209,582 @@
 														formatDate(new Date(p.endTime), 'mm-dd')
 													}}】</span
 												> -->
-											<div class="txt-flex">
-												<span v-if="p.userObj" :title="p.userObj.memberName">{{ p.userObj.memberName }}</span>
-												<span v-else-if="p.userName" :title="p.userName">{{ p.userName }}</span>
-												<span v-else>空闲</span>
-											</div>
-											<el-button v-if="!p.userObj && (p.assignStatus == '10' || p.assignStatus == '40')" link type="primary" @click="onAllocate(p)"
-												>分配</el-button
-											>
-										</li>
-									</ul>
-								</div>
-							</el-col>
-						</el-row>
-					</div>
-				</div>
-			</div>
-			<template #footer>
-				<span class="dialog-footer">
-					<el-button type="info" @click="onCancel" size="default">取 消</el-button>
-					<!-- <el-button type="success" @click="handelBatchAssign()" size="default">一键分配</el-button> -->
-					<el-button color="#2c78ff" @click="onSubmit('20')" size="default">确 定</el-button>
-				</span>
-			</template>
-		</el-dialog>
-		<DetailsDialog ref="detailsDialog" />
-	</div>
+                      <div class="txt-flex">
+                        <span
+                          v-if="p.userObj"
+                          :title="p.userObj.memberName"
+                        >
+                          {{ p.userObj.memberName }}
+                        </span>
+                        <span
+                          v-else-if="p.userName"
+                          :title="p.userName"
+                        >
+                          {{ p.userName }}
+                        </span>
+                        <span v-else>空闲</span>
+                      </div>
+                      <el-button
+                        v-if="!p.userObj && (p.assignStatus == '10' || p.assignStatus == '40')"
+                        link
+                        type="primary"
+                        @click="onAllocate(p)"
+                      >
+                        分配
+                      </el-button>
+                    </li>
+                  </ul>
+                </div>
+              </el-col>
+            </el-row>
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button
+            type="info"
+            @click="onCancel"
+            size="default"
+          >
+            取 消
+          </el-button>
+          <!-- <el-button type="success" @click="handelBatchAssign()" size="default">一键分配</el-button> -->
+          <el-button
+            color="#2c78ff"
+            @click="onSubmit('20')"
+            size="default"
+          >
+            确 定
+          </el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <DetailsDialog ref="detailsDialog" />
+  </div>
 </template>
 
 <script setup lang="ts" name="entryAllocate">
-import to from 'await-to-js';
-import { reactive, ref, defineAsyncComponent, computed } from 'vue';
-import { getDictLabel } from '/@/utils/other';
-import { ElMessage, ElMessageBox } from 'element-plus';
-import { usePlatformApi } from '/@/api/platform/home';
-import { useSystemApi } from '/@/api/platform/system';
-import { useUserInfo } from '/@/stores/userInfo';
-import { storeToRefs } from 'pinia';
-import { useDictApi } from '/@/api/base/system/dict';
-import { useCellAssignApi } from '/@/api/platform/home/assign';
-import { formatDate } from '/@/utils/formatTime';
+  import to from 'await-to-js'
+  import { reactive, ref, defineAsyncComponent, computed } from 'vue'
+  import { getDictLabel } from '/@/utils/other'
+  import { ElMessage, ElMessageBox } from 'element-plus'
+  import { usePlatformApi } from '/@/api/platform/home'
+  import { useSystemApi } from '/@/api/platform/system'
+  import { useUserInfo } from '/@/stores/userInfo'
+  import { storeToRefs } from 'pinia'
+  import { useDictApi } from '/@/api/base/system/dict'
+  import { useCellAssignApi } from '/@/api/platform/home/assign'
+  import { formatDate } from '/@/utils/formatTime'
 
-const DetailsDialog = defineAsyncComponent(() => import('/@/view/entry/components/details.vue'));
-const stores = useUserInfo();
-const { userInfos } = storeToRefs(stores);
+  const DetailsDialog = defineAsyncComponent(() => import('/@/view/entry/components/details.vue'))
+  const stores = useUserInfo()
+  const { userInfos } = storeToRefs(stores)
 
-// 定义子组件向父组件传值/事件
-const emit = defineEmits(['refresh']);
+  // 定义子组件向父组件传值/事件
+  const emit = defineEmits(['refresh'])
 
-// 定义变量内容
-const systemApi = useSystemApi();
-const platformApi = usePlatformApi();
-const dictApi = useDictApi();
-const userTypeList = ref<RowDicDataType[]>([]);
-const projectGroupList = ref<any[]>([]);
-const cellType = ref<RowDicDataType[]>([]);
-const resourceList = ref<any[]>([]);
-const molecularGroupList = ref<any[]>([]);
-const cellAssignApi = useCellAssignApi();
+  // 定义变量内容
+  const systemApi = useSystemApi()
+  const platformApi = usePlatformApi()
+  const dictApi = useDictApi()
+  const userTypeList = ref<RowDicDataType[]>([])
+  const projectGroupList = ref<any[]>([])
+  const cellType = ref<RowDicDataType[]>([])
+  const resourceList = ref<any[]>([])
+  const molecularGroupList = ref<any[]>([])
+  const cellAssignApi = useCellAssignApi()
 
-const state = reactive({
-	form: {
-		id: 0,
-		memberId: 0,
-		memberName: '',
-		memberPhone: '',
-		memberSex: '',
-		memberNo: '',
-		memberIden: '',
-		startDate: '',
-		endDate: '',
-		memberType: '',
-		deptId: 0,
-		deptName: '',
-		workPlace: '',
-		mentorId: 0,
-		mentorName: '',
-		mentorPhone: '',
-		mentorDeptId: 0,
-		mentorDeptName: '',
-		platformId: 0,
-		platformName: '',
-		platformType: '',
-		platformTime: 0,
-		other: '',
-		isTemporary: '',
-	},
-	confirmList: [] as any[],
-	resourceList: [] as any[],
-	active: -1,
-	dialog: {
-		isShowDialog: false,
-		type: '',
-		title: '',
-		submitTxt: '',
-	},
-});
-const comfirmTotal = computed(() => {
-	return state.confirmList.reduce((pre: number, cur: any) => {
-		if (cur.resId) return pre + 1;
-		return pre;
-	}, 0);
-});
-const confirmList = ref<any[]>([]);
-const belongOrgOption = ref<any[]>([]);
-const userList = ref<any[]>([]);
-const platformList = ref();
-const resClass = ref('');
-const groupId = ref(null); // 资源组ID
-const detailsDialog = ref();
-const curSelectedResourceId = ref(0); // 当前选中的资源ID
-const allocationSituationList = ref<any[]>([]); // 资源分配情况列表
-const getDicts = () => {
-	Promise.all([
-		systemApi.getDeptTree(),
-		systemApi.getUserList({ noPage: true }),
-		platformApi.getAllPlatformList({ noPage: true }),
-		dictApi.getDictDataByType('sys_user_type'),
-		systemApi.getProjectGroupList({ noPage: true }),
-		userInfos.value.platformId != 0 ? platformApi.getResourceTypeDict({ id: userInfos.value.platformId }) : '',
-	]).then(([dept, user, plat, type, pjt, cell]) => {
-		belongOrgOption.value = dept?.data || [];
-		userList.value = user?.data?.list || [];
-		platformList.value = plat?.data?.list || [];
-		userTypeList.value = type?.data?.values || [];
-		projectGroupList.value = pjt?.data?.list || [];
-		cellType.value = cell?.data || [];
-	});
-};
-const getMolecularGroupList = async () => {
-	const [err, res]: ToResponse = await to(platformApi.getMolecularGroupList({ id: userInfos.value.platformId }));
-	if (err) return;
-	molecularGroupList.value = res?.data || [];
-};
-const getResourceList = async () => {
-	const [err, res]: ToResponse = await to(platformApi.getResourceList({ platformId: userInfos.value.platformId, resStatus: '10' }));
-	if (err) return;
-	resourceList.value = res?.data?.list || [];
-	state.resourceList = JSON.parse(JSON.stringify(resourceList.value));
-};
-// 待分配列表
-const getConfirmList = async () => {
-	const [err, res]: ToResponse = await to(cellAssignApi.assignQueue({ platformId: userInfos.value.platformId }));
-	if (err) return;
-	confirmList.value = res?.data || [];
-	state.confirmList = JSON.parse(JSON.stringify(confirmList.value));
-};
-// 打开弹窗
-const openDialog = async (type: string, row: number) => {
-	getDicts();
-	getResourceList();
-	getConfirmList();
-	getMolecularGroupList();
-	state.dialog.type = type;
-	state.dialog.title = '入室资源分配';
-	state.dialog.isShowDialog = true;
-};
-// 前端筛选类型
-const filterOutList = (val: string) => {
-	if (val) {
-		state.resourceList = resourceList.value.filter((item: any) => item.resClass == val);
-		state.confirmList = confirmList.value.filter((item: any) => item.cellSourceType == val);
-	} else {
-		state.resourceList = [...resourceList.value];
-		state.confirmList = [...confirmList.value];
-	}
-};
-// 前端筛选资源组
-const filterOutListByGroupId = (val: string) => {
-	if (val) {
-		state.resourceList = resourceList.value.filter((item: any) => item.groupId == val);
-	} else {
-		state.resourceList = [...resourceList.value];
-	}
-};
-// 选择资源房间
-const onSelectResource = (row: any) => {
-	curSelectedResourceId.value = row.id;
-	allocationSituationList.value = row.usedList;
-};
-// 确认
-const onConfirm = (row: any) => {
-	// 验证同类型 下个月出室人员 = 已确认人数 提示无法确认
-};
-// 关闭弹窗
-const closeDialog = () => {
-	state.resourceList = [];
-	state.confirmList = [];
-	resourceList.value = [];
-	confirmList.value = [];
-	state.dialog.isShowDialog = false;
-};
-// 取消
-const onCancel = () => {
-	closeDialog();
-};
+  const state = reactive({
+    form: {
+      id: 0,
+      memberId: 0,
+      memberName: '',
+      memberPhone: '',
+      memberSex: '',
+      memberNo: '',
+      memberIden: '',
+      startDate: '',
+      endDate: '',
+      memberType: '',
+      deptId: 0,
+      deptName: '',
+      workPlace: '',
+      mentorId: 0,
+      mentorName: '',
+      mentorPhone: '',
+      mentorDeptId: 0,
+      mentorDeptName: '',
+      platformId: 0,
+      platformName: '',
+      platformType: '',
+      platformTime: 0,
+      other: '',
+      isTemporary: '',
+    },
+    confirmList: [] as any[],
+    resourceList: [] as any[],
+    active: -1,
+    dialog: {
+      isShowDialog: false,
+      type: '',
+      title: '',
+      submitTxt: '',
+    },
+  })
+  const comfirmTotal = computed(() => {
+    return state.confirmList.reduce((pre: number, cur: any) => {
+      if (cur.resId) return pre + 1
+      return pre
+    }, 0)
+  })
+  const confirmList = ref<any[]>([])
+  const belongOrgOption = ref<any[]>([])
+  const userList = ref<any[]>([])
+  const platformList = ref()
+  const resClass = ref('')
+  const groupId = ref(null) // 资源组ID
+  const detailsDialog = ref()
+  const curSelectedResourceId = ref(0) // 当前选中的资源ID
+  const allocationSituationList = ref<any[]>([]) // 资源分配情况列表
+  const getDicts = () => {
+    Promise.all([
+      systemApi.getDeptTree(),
+      systemApi.getUserList({ noPage: true }),
+      platformApi.getAllPlatformList({ noPage: true }),
+      dictApi.getDictDataByType('sys_user_type'),
+      systemApi.getProjectGroupList({ noPage: true }),
+      userInfos.value.platformId != 0 ? platformApi.getResourceTypeDict({ id: userInfos.value.platformId }) : '',
+    ]).then(([dept, user, plat, type, pjt, cell]) => {
+      belongOrgOption.value = dept?.data || []
+      userList.value = user?.data?.list || []
+      platformList.value = plat?.data?.list || []
+      userTypeList.value = type?.data?.values || []
+      projectGroupList.value = pjt?.data?.list || []
+      cellType.value = cell?.data || []
+    })
+  }
+  const getMolecularGroupList = async () => {
+    const [err, res]: ToResponse = await to(platformApi.getMolecularGroupList({ id: userInfos.value.platformId }))
+    if (err) return
+    molecularGroupList.value = res?.data || []
+  }
+  const getResourceList = async () => {
+	console.log(userInfos.value, 'dsaddada')
+    const [err, res]: ToResponse = await to(
+      platformApi.getResourceList({ platformId: userInfos.value.platformId, resStatus: '10' }),
+    )
+    if (err) return
+    resourceList.value = res?.data?.list || []
+    state.resourceList = JSON.parse(JSON.stringify(resourceList.value))
+  }
+  // 待分配列表
+  const getConfirmList = async () => {
+    const [err, res]: ToResponse = await to(cellAssignApi.assignQueue({ platformId: userInfos.value.platformId }))
+    if (err) return
+    confirmList.value = res?.data || []
+    state.confirmList = JSON.parse(JSON.stringify(confirmList.value))
+  }
+  // 打开弹窗
+  const openDialog = async (type: string, row: number) => {
+    getDicts()
+    getResourceList()
+    getConfirmList()
+    getMolecularGroupList()
+    state.dialog.type = type
+    state.dialog.title = '入室资源分配'
+    state.dialog.isShowDialog = true
+  }
+  // 前端筛选类型
+  const filterOutList = (val: string) => {
+    if (val) {
+      state.resourceList = resourceList.value.filter((item: any) => item.resClass == val)
+      state.confirmList = confirmList.value.filter((item: any) => item.cellSourceType == val)
+    } else {
+      state.resourceList = [...resourceList.value]
+      state.confirmList = [...confirmList.value]
+    }
+  }
+  // 前端筛选资源组
+  const filterOutListByGroupId = (val: string) => {
+    if (val) {
+      state.resourceList = resourceList.value.filter((item: any) => item.groupId == val)
+    } else {
+      state.resourceList = [...resourceList.value]
+    }
+  }
+  // 选择资源房间
+  const onSelectResource = (row: any) => {
+    curSelectedResourceId.value = row.id
+    allocationSituationList.value = row.usedList
+  }
+  // 确认
+  const onConfirm = (row: any) => {
+    // 验证同类型 下个月出室人员 = 已确认人数 提示无法确认
+  }
+  // 关闭弹窗
+  const closeDialog = () => {
+    state.resourceList = []
+    state.confirmList = []
+    resourceList.value = []
+    confirmList.value = []
+    state.dialog.isShowDialog = false
+  }
+  // 取消
+  const onCancel = () => {
+    closeDialog()
+  }
 
-const handelBatchAssign = async () => {
-	const checkedUser = state.confirmList.filter((item: any) => item.checked);
-	console.log(checkedUser);
-	if (!checkedUser.length) {
-		ElMessage.warning('请选择待分配队列人员');
-		return;
-	}
-	ElMessageBox.confirm(`是否将选中队列人员一键分配资源?`, '提示', {
-		confirmButtonText: '确认',
-		cancelButtonText: '取消',
-		type: 'warning',
-	})
-		.then(async () => {
-			const [err]: ToResponse = await to(platformApi.batchAssign({ appointIds: checkedUser.map((item: any) => item.id) }));
-			if (err) return;
-			ElMessage.warning('一键分配成功');
-			getResourceList();
-			getConfirmList();
-		})
-		.catch(() => {});
-};
-// 提交
-const onSubmit = async (type: string) => {
-	const arr = confirmList.value.filter((item: any) => item.resId);
-	if (!arr.length) {
-		ElMessage.warning('请分配资源');
-		return;
-	}
+  const handelBatchAssign = async () => {
+    const checkedUser = state.confirmList.filter((item: any) => item.checked)
+    console.log(checkedUser)
+    if (!checkedUser.length) {
+      ElMessage.warning('请选择待分配队列人员')
+      return
+    }
+    ElMessageBox.confirm(`是否将选中队列人员一键分配资源?`, '提示', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    })
+      .then(async () => {
+        const [err]: ToResponse = await to(
+          platformApi.batchAssign({ appointIds: checkedUser.map((item: any) => item.id) }),
+        )
+        if (err) return
+        ElMessage.warning('一键分配成功')
+        getResourceList()
+        getConfirmList()
+      })
+      .catch(() => {})
+  }
+  // 提交
+  const onSubmit = async (type: string) => {
+    const arr = confirmList.value.filter((item: any) => item.resId)
+    if (!arr.length) {
+      ElMessage.warning('请分配资源')
+      return
+    }
 
-	const [err]: ToResponse = await to(
-		cellAssignApi.create({
-			assignList: arr.map((item: any) => {
-				return {
-					appointId: item.id,
-					resId: item.resId,
-					location: item.location,
-					replaceId: item.replaceId,
-					mainId: item.id || 0,
-					platformType:item.platformType || '10',
-				};
-			}),
-		})
-	);
-	if (err) return;
-	ElMessage.success('操作成功');
-	emit('refresh');
-	closeDialog();
-};
-const onDetail = (val: number) => {
-	detailsDialog.value.openDialog('get', val);
-};
-// 分配
-const onAllocate = (pos: any) => {
-	if (state.active < 0) {
-		ElMessage.warning('请先选择一个待分配人员');
-		return;
-	}
-	const activeObj = state.confirmList[state.active];
-	const room = state.resourceList.find((item: any) => item.id == curSelectedResourceId.value);
-	if (room.resClass != activeObj.cellSourceType) {
-		ElMessage.warning('请选择正确的资源');
-		return;
-	}
-	activeObj.resId = room.id;
-	activeObj.resName = room.resName;
-	activeObj.location = pos.location;
-	activeObj.replaceId = pos.id;
-	const user = confirmList.value.find((item: any) => item.id == activeObj.id);
-	user.resId = room.id;
-	user.resName = room.resName;
-	user.location = pos.location;
-	user.replaceId = pos.id;
-	pos.userObj = { ...activeObj };
-	const resource = resourceList.value.find((item: any) => item.id == room.id);
-	const used = resource.usedList.find((item: any) => item.location == pos.location);
-	used.userObj = { ...activeObj };
-};
-// 取消分配
-const cancelAllocate = (obj: any) => {
-	for (const room of resourceList.value) {
-		if (room.id == obj.resId) {
-			const used = room.usedList.find((i: any) => {
-				return i.location == obj.location;
-			});
-			used.userObj = null;
-		}
-	}
-	const user = confirmList.value.find((item) => item.resId == obj.resId);
-	user.resId = null;
-	user.resName = null;
-	user.location = null;
-	user.replaceId = null;
-	filterOutList(resClass.value);
-};
-const getUsedClass = (row: any) => {
-	if (row.assignStatus == '10') {
-		// 空
-		if (row.userObj) return 'empty-allocate';
-		return 'empty';
-	} else if (row.assignStatus == '40') {
-		// 次月离室
-		if (row.userObj) return 'leave-allocate';
-		return 'leave';
-	} else {
-		// 占用20 已分配45
-		return 'used';
-	}
-};
-// 暴露变量
-defineExpose({
-	openDialog,
-});
+    const [err]: ToResponse = await to(
+      cellAssignApi.create({
+        assignList: arr.map((item: any) => {
+          return {
+            appointId: item.id,
+            resId: item.resId,
+            location: item.location,
+            replaceId: item.replaceId,
+            mainId: item.id || 0,
+            platformType: item.platformType || '10',
+          }
+        }),
+      }),
+    )
+    if (err) return
+    ElMessage.success('操作成功')
+    emit('refresh')
+    closeDialog()
+  }
+  const onDetail = (val: number) => {
+    detailsDialog.value.openDialog('get', val)
+  }
+  // 分配
+  const onAllocate = (pos: any) => {
+    if (state.active < 0) {
+      ElMessage.warning('请先选择一个待分配人员')
+      return
+    }
+    const activeObj = state.confirmList[state.active]
+    const room = state.resourceList.find((item: any) => item.id == curSelectedResourceId.value)
+    if (room.resClass != activeObj.cellSourceType) {
+      ElMessage.warning('请选择正确的资源')
+      return
+    }
+    activeObj.resId = room.id
+    activeObj.resName = room.resName
+    activeObj.location = pos.location
+    activeObj.replaceId = pos.id
+    const user = confirmList.value.find((item: any) => item.id == activeObj.id)
+    user.resId = room.id
+    user.resName = room.resName
+    user.location = pos.location
+    user.replaceId = pos.id
+    pos.userObj = { ...activeObj }
+    const resource = resourceList.value.find((item: any) => item.id == room.id)
+    const used = resource.usedList.find((item: any) => item.location == pos.location)
+    used.userObj = { ...activeObj }
+  }
+  // 取消分配
+  const cancelAllocate = (obj: any) => {
+    for (const room of resourceList.value) {
+      if (room.id == obj.resId) {
+        const used = room.usedList.find((i: any) => {
+          return i.location == obj.location
+        })
+        used.userObj = null
+      }
+    }
+    const user = confirmList.value.find((item) => item.resId == obj.resId)
+    user.resId = null
+    user.resName = null
+    user.location = null
+    user.replaceId = null
+    filterOutList(resClass.value)
+  }
+  const getUsedClass = (row: any) => {
+    if (row.assignStatus == '10') {
+      // 空
+      if (row.userObj) return 'empty-allocate'
+      return 'empty'
+    } else if (row.assignStatus == '40') {
+      // 次月离室
+      if (row.userObj) return 'leave-allocate'
+      return 'leave'
+    } else {
+      // 占用20 已分配45
+      return 'used'
+    }
+  }
+  // 暴露变量
+  defineExpose({
+    openDialog,
+  })
 </script>
 <style lang="scss" scoped>
-h4 {
-	font-size: 18px;
-}
+  h4 {
+    font-size: 18px;
+  }
 
-header {
-	display: flex;
-	justify-content: space-between;
-	align-items: center;
-	height: 32px;
-	line-height: 32px;
+  header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 32px;
+    line-height: 32px;
 
-	.el-form.el-form--inline .el-form-item {
-		margin: 0;
-	}
-}
+    .el-form.el-form--inline .el-form-item {
+      margin: 0;
+    }
+  }
 
-.table-container {
-	/* display: flex; */
-	.use-table {
-		/* width: 240px !important; */
-	}
-	.out-table {
-		/* width: 848px; */
-		.out-table-wrap {
-			height: 500px;
-			.out-table-wrap-row {
-				height: 100%;
-			}
-			.resourceList-col {
-				height: 100%;
-				overflow: auto;
-			}
-		}
-		.items {
-			height: 146px;
-			padding: 12px;
-			border-radius: 4px;
-			background-color: #f2f2f2;
-			display: flex;
-			flex-direction: column;
-			justify-content: space-between;
-			overflow: hidden;
-			cursor: pointer;
-			&:hover {
-				box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3);
-			}
-			&.active {
-				background: #ebf3ff;
-				box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3);
-			}
-			header {
-				display: flex;
-				align-items: center;
-				justify-content: space-between;
+  .table-container {
+    /* display: flex; */
+    .use-table {
+      /* width: 240px !important; */
+    }
+    .out-table {
+      /* width: 848px; */
+      .out-table-wrap {
+        height: 500px;
+        .out-table-wrap-row {
+          height: 100%;
+        }
+        .resourceList-col {
+          height: 100%;
+          overflow: auto;
+        }
+      }
+      .items {
+        height: 146px;
+        padding: 12px;
+        border-radius: 4px;
+        background-color: #f2f2f2;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        overflow: hidden;
+        cursor: pointer;
+        &:hover {
+          box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3);
+        }
+        &.active {
+          background: #ebf3ff;
+          box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3);
+        }
+        header {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
 
-				.title {
-					display: flex;
-					align-items: center;
+          .title {
+            display: flex;
+            align-items: center;
 
-					.el-switch {
-						line-height: 24px;
-					}
-				}
-			}
+            .el-switch {
+              line-height: 24px;
+            }
+          }
+        }
 
-			footer {
-				display: flex;
-				align-items: center;
+        footer {
+          display: flex;
+          align-items: center;
 
-				ul {
-					flex: 1;
-					list-style: none;
-					display: flex;
-					flex-wrap: wrap;
+          ul {
+            flex: 1;
+            list-style: none;
+            display: flex;
+            flex-wrap: wrap;
 
-					li {
-						flex: 0 0 20%;
-						display: flex;
-						justify-content: center;
-						margin-bottom: 2px;
+            li {
+              flex: 0 0 20%;
+              display: flex;
+              justify-content: center;
+              margin-bottom: 2px;
 
-						&::before {
-							display: block;
-							content: '';
-							height: 10px;
-							width: 10px;
-							border-radius: 50%;
-							background-color: #70b603;
-						}
+              &::before {
+                display: block;
+                content: '';
+                height: 10px;
+                width: 10px;
+                border-radius: 50%;
+                background-color: #70b603;
+              }
 
-						&.used::before {
-							background-color: #fe5d5a;
-						}
-						&.empty-allocate::before {
-							background: linear-gradient(to right, #70b603 0%, #70b603 50%, #2c78ff 50%, #2c78ff 100%);
-						}
-						&.leave::before {
-							background-color: #f59a23;
-						}
-						&.leave-allocate::before {
-							background: linear-gradient(to right, #f59a23 0%, #f59a23 50%, #2c78ff 50%, #2c78ff 100%);
-						}
-					}
-				}
-			}
+              &.used::before {
+                background-color: #fe5d5a;
+              }
+              &.empty-allocate::before {
+                background: linear-gradient(to right, #70b603 0%, #70b603 50%, #2c78ff 50%, #2c78ff 100%);
+              }
+              &.leave::before {
+                background-color: #f59a23;
+              }
+              &.leave-allocate::before {
+                background: linear-gradient(to right, #f59a23 0%, #f59a23 50%, #2c78ff 50%, #2c78ff 100%);
+              }
+            }
+          }
+        }
 
-			.el-icon {
-				font-size: 20px !important;
-			}
-		}
-	}
-}
+        .el-icon {
+          font-size: 20px !important;
+        }
+      }
+    }
+  }
 
-.confirm-table {
-	margin-right: 12px;
-	width: 300px;
-	display: flex;
-	flex-direction: column;
-	ul {
-		height: 500px;
-		overflow-y: auto;
-		li {
-			display: flex;
-			align-items: center;
-			justify-content: space-between;
-			border-radius: 4px;
-			border: 1px solid #eee;
-			padding: 8px;
-			cursor: pointer;
-			& + li {
-				margin-top: 8px;
-			}
-			&.active {
-				background-color: #ebf3ff;
-			}
-			.text {
-				flex: 1;
-			}
-			.assignRoom {
-				font-weight: bold;
-				color: #2c78ff;
-			}
-			.el-icon {
-				color: #fff;
-				background-color: #0dda83;
-				font-size: 20px;
-				padding: 4px;
-				border-radius: 50%;
-				margin: 0 12px;
-			}
-			.btns {
-				display: flex;
-				flex-direction: column;
-				.el-button {
-					margin-left: 0;
-				}
-			}
-		}
-	}
-}
+  .confirm-table {
+    margin-right: 12px;
+    width: 300px;
+    display: flex;
+    flex-direction: column;
+    ul {
+      height: 500px;
+      overflow-y: auto;
+      li {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        border-radius: 4px;
+        border: 1px solid #eee;
+        padding: 8px;
+        cursor: pointer;
+        & + li {
+          margin-top: 8px;
+        }
+        &.active {
+          background-color: #ebf3ff;
+        }
+        .text {
+          flex: 1;
+        }
+        .assignRoom {
+          font-weight: bold;
+          color: #2c78ff;
+        }
+        .el-icon {
+          color: #fff;
+          background-color: #0dda83;
+          font-size: 20px;
+          padding: 4px;
+          border-radius: 50%;
+          margin: 0 12px;
+        }
+        .btns {
+          display: flex;
+          flex-direction: column;
+          .el-button {
+            margin-left: 0;
+          }
+        }
+      }
+    }
+  }
 
-:deep(.disUoloadSty .el-upload--picture-card) {
-	display: none;
-	/* 上传按钮隐藏 */
-}
+  :deep(.disUoloadSty .el-upload--picture-card) {
+    display: none;
+    /* 上传按钮隐藏 */
+  }
 
-.avatar-uploader {
-	:deep(.el-upload) {
-		width: 100%;
-		height: 100%;
-		border: 1px dashed var(--el-border-color);
-		border-radius: 6px;
-		cursor: pointer;
-		position: relative;
-		overflow: hidden;
-		transition: var(--el-transition-duration-fast);
+  .avatar-uploader {
+    :deep(.el-upload) {
+      width: 100%;
+      height: 100%;
+      border: 1px dashed var(--el-border-color);
+      border-radius: 6px;
+      cursor: pointer;
+      position: relative;
+      overflow: hidden;
+      transition: var(--el-transition-duration-fast);
 
-		&:hover {
-			border-color: var(--el-color-primary);
-		}
-	}
-}
+      &:hover {
+        border-color: var(--el-color-primary);
+      }
+    }
+  }
 
-.el-icon.avatar-uploader-icon {
-	font-size: 28px;
-	color: #8c939d;
-	width: 178px;
-	height: 178px;
-	text-align: center;
-}
-.allocation-situation-col {
-	height: 100%;
-	.allocation-situation {
-		height: 100%;
-		overflow: auto;
-		.position-choose {
-			list-style: none;
-			border: 1px solid #eee;
-			height: 100%;
-			overflow: auto;
-			li {
-				height: 32px;
-				line-height: 32px;
-				display: flex;
-				// justify-content: space-between;
-				align-items: center;
-				.txt-flex {
-					flex: 1;
-					white-space: nowrap;
-					overflow: hidden;
-					text-overflow: ellipsis;
-				}
-				i {
-					display: inline-block;
-					height: 10px;
-					width: 10px;
-					border-radius: 50%;
-					margin: 0 4px;
-					background-color: #70b603;
-					&.used {
-						background-color: #fe5d5a;
-					}
-					&.empty-allocate {
-						background: linear-gradient(to right, #70b603 0%, #70b603 50%, #2c78ff 50%, #2c78ff 100%);
-					}
-					&.leave {
-						background-color: #f59a23;
-					}
-					&.leave-allocate {
-						background: linear-gradient(to right, #f59a23 0%, #f59a23 50%, #2c78ff 50%, #2c78ff 100%);
-					}
-				}
-			}
-		}
-	}
-}
+  .el-icon.avatar-uploader-icon {
+    font-size: 28px;
+    color: #8c939d;
+    width: 178px;
+    height: 178px;
+    text-align: center;
+  }
+  .allocation-situation-col {
+    height: 100%;
+    .allocation-situation {
+      height: 100%;
+      overflow: auto;
+      .position-choose {
+        list-style: none;
+        border: 1px solid #eee;
+        height: 100%;
+        overflow: auto;
+        li {
+          height: 32px;
+          line-height: 32px;
+          display: flex;
+          // justify-content: space-between;
+          align-items: center;
+          .txt-flex {
+            flex: 1;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+          i {
+            display: inline-block;
+            height: 10px;
+            width: 10px;
+            border-radius: 50%;
+            margin: 0 4px;
+            background-color: #70b603;
+            &.used {
+              background-color: #fe5d5a;
+            }
+            &.empty-allocate {
+              background: linear-gradient(to right, #70b603 0%, #70b603 50%, #2c78ff 50%, #2c78ff 100%);
+            }
+            &.leave {
+              background-color: #f59a23;
+            }
+            &.leave-allocate {
+              background: linear-gradient(to right, #f59a23 0%, #f59a23 50%, #2c78ff 50%, #2c78ff 100%);
+            }
+          }
+        }
+      }
+    }
+  }
 </style>

+ 215 - 46
src/view/entry/manage.vue

@@ -1,41 +1,152 @@
 <template>
   <div class="entry-container">
     <div class="list-container">
-      <div class="search-wrap" ref="searchWrapRef">
-        <el-form :model="state.queryParams" ref="queryRef">
+      <div
+        class="search-wrap"
+        ref="searchWrapRef"
+      >
+        <el-form
+          :model="state.queryParams"
+          ref="queryRef"
+        >
+          <el-form-item prop="serialNo">
+            <el-select
+              v-model="state.queryParams.platformType"
+              placeholder="申请平台"
+              clearable
+              @change="search"
+            >
+              <el-option
+                label="细胞平台"
+                value="10"
+              ></el-option>
+              <el-option
+                label="分子平台"
+                value="20"
+              ></el-option>
+            </el-select>
+          </el-form-item>
           <el-form-item prop="serialNo">
             <div class="flex justify-between">
-              <el-date-picker v-model="startTime" style="width: 48%" type="date" placeholder="开始时间" clearable @change="search" />
-              <el-date-picker v-model="endTime" style="width: 48%" type="date" placeholder="结束时间" clearable @change="search" />
+              <el-date-picker
+                v-model="startTime"
+                style="width: 48%"
+                type="date"
+                placeholder="开始时间"
+                clearable
+                @change="search"
+              />
+              <el-date-picker
+                v-model="endTime"
+                style="width: 48%"
+                type="date"
+                placeholder="结束时间"
+                clearable
+                @change="search"
+              />
             </div>
           </el-form-item>
 
-          <el-form-item style="margin-bottom: 0" prop="serialNo">
-            <el-input v-model="state.queryParams.memberName" placeholder="入室申请名称" clearable @keyup.enter="search" />
+          <el-form-item
+            style="margin-bottom: 0"
+            prop="serialNo"
+          >
+            <el-input
+              v-model="state.queryParams.memberName"
+              placeholder="入室申请名称"
+              clearable
+              @keyup.enter="search"
+            />
           </el-form-item>
         </el-form>
         <div class="buttons">
-          <el-button v-auth="'platform_entry_allocate'" color="#2c78ff" @click="onAllocate()">分配</el-button>
-          <el-button v-auth="'platform_entry_confirm'" color="#2c78ff" @click="onConfirm()">确认</el-button>
+          <el-button
+            v-auth="'platform_entry_allocate'"
+            color="#2c78ff"
+            @click="onAllocate()"
+          >
+            分配
+          </el-button>
+          <el-button
+            v-auth="'platform_entry_confirm'"
+            color="#2c78ff"
+            @click="onConfirm()"
+          >
+            确认
+          </el-button>
         </div>
       </div>
 
-      <van-list v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
-        <van-cell v-for="item in state.list" :key="item" @click="toDetail(item.id)">
+      <van-list
+        v-model:loading="state.loading"
+        :finished="state.finished"
+        finished-text="没有更多了"
+        @load="onLoad"
+      >
+        <van-cell
+          v-for="item in state.list"
+          :key="item"
+          @click="toDetail(item.id)"
+        >
           <template #default>
             <div class="list">
               <header class="flex justify-between">
                 <strong class="title">{{ `${item.memberName}的${item.platformName}入室申请` }}</strong>
 
-                <van-tag v-if="item.appointStatus === ApplicationStatus.IN_QUEUE" type="primary">队列中</van-tag>
-                <van-tag v-else-if="item.appointStatus === ApplicationStatus.PENDING_UPLOAD" type="primary">待上传</van-tag>
-                <van-tag v-else-if="item.appointStatus === ApplicationStatus.PENDING_REVIEW" type="primary">待审核</van-tag>
-                <van-tag v-else-if="item.appointStatus === ApplicationStatus.REJECTED" type="danger">已驳回</van-tag>
-                <van-tag v-else-if="item.appointStatus === ApplicationStatus.WITHDRAWN" type="warning">已撤回</van-tag>
-                <van-tag v-else-if="item.appointStatus === ApplicationStatus.PENDING_ASSIGNMENT" type="primary">待分配</van-tag>
-                <van-tag v-else-if="item.appointStatus === ApplicationStatus.AVAILABLE_FOR_ADMISSION" type="success">可入室</van-tag>
-                <van-tag v-else-if="item.appointStatus === ApplicationStatus.OUT_OF_ROOM" type="primary">已出室</van-tag>
-                <van-tag v-else type="primary">初始</van-tag>
+                <van-tag
+                  v-if="item.appointStatus === ApplicationStatus.IN_QUEUE"
+                  type="primary"
+                >
+                  队列中
+                </van-tag>
+                <van-tag
+                  v-else-if="item.appointStatus === ApplicationStatus.PENDING_UPLOAD"
+                  type="primary"
+                >
+                  待上传
+                </van-tag>
+                <van-tag
+                  v-else-if="item.appointStatus === ApplicationStatus.PENDING_REVIEW"
+                  type="primary"
+                >
+                  待审核
+                </van-tag>
+                <van-tag
+                  v-else-if="item.appointStatus === ApplicationStatus.REJECTED"
+                  type="danger"
+                >
+                  已驳回
+                </van-tag>
+                <van-tag
+                  v-else-if="item.appointStatus === ApplicationStatus.WITHDRAWN"
+                  type="warning"
+                >
+                  已撤回
+                </van-tag>
+                <van-tag
+                  v-else-if="item.appointStatus === ApplicationStatus.PENDING_ASSIGNMENT"
+                  type="primary"
+                >
+                  待分配
+                </van-tag>
+                <van-tag
+                  v-else-if="item.appointStatus === ApplicationStatus.AVAILABLE_FOR_ADMISSION"
+                  type="success"
+                >
+                  可入室
+                </van-tag>
+                <van-tag
+                  v-else-if="item.appointStatus === ApplicationStatus.OUT_OF_ROOM"
+                  type="primary"
+                >
+                  已出室
+                </van-tag>
+                <van-tag
+                  v-else
+                  type="primary"
+                >
+                  初始
+                </van-tag>
               </header>
               <p class="inst-title">
                 <span>课题名称</span>
@@ -44,11 +155,36 @@
               <p class="inst-title">
                 <span>审批状态</span>
 
-                <span class="title ml8" v-if="item.approveStatus === ApproveStatus.NEED_SUBMIT">待提交</span>
-                <span class="title ml8" v-else-if="item.approveStatus === ApproveStatus.IN_REVIEW">审批中</span>
-                <span class="title ml8" v-else-if="item.approveStatus === ApproveStatus.APPROVED">审批通过</span>
-                <span class="title ml8" v-else-if="item.approveStatus === ApproveStatus.REJECTED">审批退回</span>
-                <span class="title ml8" v-else-if="item.approveStatus === ApproveStatus.WITHDRAWN">审批撤回</span>
+                <span
+                  class="title ml8"
+                  v-if="item.approveStatus === ApproveStatus.NEED_SUBMIT"
+                >
+                  待提交
+                </span>
+                <span
+                  class="title ml8"
+                  v-else-if="item.approveStatus === ApproveStatus.IN_REVIEW"
+                >
+                  审批中
+                </span>
+                <span
+                  class="title ml8"
+                  v-else-if="item.approveStatus === ApproveStatus.APPROVED"
+                >
+                  审批通过
+                </span>
+                <span
+                  class="title ml8"
+                  v-else-if="item.approveStatus === ApproveStatus.REJECTED"
+                >
+                  审批退回
+                </span>
+                <span
+                  class="title ml8"
+                  v-else-if="item.approveStatus === ApproveStatus.WITHDRAWN"
+                >
+                  审批撤回
+                </span>
               </p>
               <p class="inst-title">
                 <span>申请人</span>
@@ -70,15 +206,20 @@
               </p>
               <p class="inst-title">
                 <span>申请时长</span>
-                <span class="title ml8"> {{ item.platformTime }}个月 </span>
+                <span class="title ml8">{{ item.platformTime }}个月</span>
               </p>
               <p class="inst-title">
                 <span>入室周期</span>
-                <span class="title ml8">{{
-                  item.appointEndDate
-                    ? `${formatDate(new Date(item.appointEndDate), 'YYYY-mm-dd')}~${formatDate(new Date(item.appointEndDate), 'YYYY-mm-dd')}`
-                    : '-'
-                }}</span>
+                <span class="title ml8">
+                  {{
+                    item.appointEndDate
+                      ? `${formatDate(new Date(item.appointEndDate), 'YYYY-mm-dd')}~${formatDate(
+                          new Date(item.appointEndDate),
+                          'YYYY-mm-dd',
+                        )}`
+                      : '-'
+                  }}
+                </span>
               </p>
               <p class="inst-title">
                 <span>费用(¥)</span>
@@ -93,37 +234,64 @@
                   v-auth="'platform_entry_bill'"
                   link
                   type="primary"
-                  v-if="item.appointStatus === ApplicationStatus.PENDING_UPLOAD && item.downLoadButton && item.createdBy === userInfos.id"
+                  v-if="
+                    item.appointStatus === ApplicationStatus.PENDING_UPLOAD &&
+                    item.downLoadButton &&
+                    item.createdBy === userInfos.id
+                  "
                   @click.stop="onDownload(item.id)"
-                  >缴费单</van-button
                 >
+                  缴费单
+                </van-button>
                 <van-button
                   v-auth="'platform_entry_upload'"
                   link
                   type="primary"
                   v-if="item.appointStatus === '10' && item.downLoadButton && item.createdBy === userInfos.id"
                   @click.stop="onUpload(item, 'add')"
-                  >上传</van-button
                 >
-                <van-button v-auth="'platform_entry_audit'" link type="primary" v-if="item.appointStatus === '20'" @click.stop="onAudit(item)">审核</van-button>
+                  上传
+                </van-button>
+                <van-button
+                  v-auth="'platform_entry_audit'"
+                  link
+                  type="primary"
+                  v-if="item.appointStatus === '20'"
+                  @click.stop="onAudit(item)"
+                >
+                  审核
+                </van-button>
                 <van-button
                   v-auth="'platform_entry_upload'"
                   link
                   type="primary"
                   v-if="item.appointStatus === '30' && item.createdBy === userInfos.id"
                   @click.stop="onUpload(item, 'edit')"
-                  >重新上传</van-button
                 >
+                  重新上传
+                </van-button>
               </footer>
             </div>
           </template>
         </van-cell>
       </van-list>
     </div>
-    <UploadDialog ref="uploadRef" @refresh="onLoad" />
-    <DetailsDialog ref="detailsDialog" @refresh="onLoad" />
-    <AllocateDialog ref="allocateRef" @refresh="onLoad" />
-    <ConfirmDialog ref="confirmRef" @refresh="onLoad" />
+    <UploadDialog
+      ref="uploadRef"
+      @refresh="onLoad"
+    />
+    <DetailsDialog
+      ref="detailsDialog"
+      @refresh="onLoad"
+    />
+    <AllocateDialog
+      ref="allocateRef"
+      @refresh="onLoad"
+    />
+    <ConfirmDialog
+      ref="confirmRef"
+      @refresh="onLoad"
+    />
   </div>
 </template>
 
@@ -153,7 +321,7 @@
     WITHDRAWN = '35',
     PENDING_ASSIGNMENT = '40',
     AVAILABLE_FOR_ADMISSION = '50',
-    OUT_OF_ROOM = '60'
+    OUT_OF_ROOM = '60',
   }
 
   // 10 待提交 20 审批中 30 审批通过 40 审批退回 35 审批撤回
@@ -162,7 +330,7 @@
     IN_REVIEW = '20',
     APPROVED = '30',
     REJECTED = '40',
-    WITHDRAWN = '35'
+    WITHDRAWN = '35',
   }
 
   const storesUserInfo = useUserInfo()
@@ -182,11 +350,12 @@
       pageNum: 1,
       pageSize: 10,
       dateTime: [],
-      memberName: ''
+      memberName: '',
+      platformType: '10',
     },
     finished: false,
     loading: true,
-    list: [] as any[]
+    list: [] as any[],
   })
 
   const onLoad = async () => {
@@ -207,8 +376,8 @@
     router.push({
       path: '/entry/detail',
       query: {
-        id
-      }
+        id,
+      },
     })
   }
 
@@ -239,7 +408,7 @@
     state.queryParams = {
       ...state.queryParams,
       pageNum: 1,
-      pageSize: 20
+      pageSize: 20,
     }
     state.list = []
     state.finished = false

+ 61 - 26
src/view/instr/calendar.vue

@@ -10,27 +10,56 @@
   <div class="calendar-container">
     <header>
       <p>
-        <strong><van-icon name="calendar-o" :size="16" />预约时间:</strong>
+        <strong>
+          <van-icon
+            name="calendar-o"
+            :size="16"
+          />
+          预约时间:
+        </strong>
         <span v-if="state.selected.length === 0">请选择开始时间段</span>
-        <span v-else-if="state.selected.length === 1">{{ formatDate(new Date(state.selected[0].startStamp), 'mm-dd HH:MM') }}~{{ formatDate(new Date(state.selected[0].endStamp), 'mm-dd HH:MM') }}</span>
-        <span v-else-if="state.selected.length === 2">{{ formatDate(new Date(state.selected[0].startStamp), 'mm-dd HH:MM') }}~{{ formatDate(new Date(state.selected[1].endStamp), 'mm-dd HH:MM') }}</span>
+        <span v-else-if="state.selected.length === 1">
+          {{ formatDate(new Date(state.selected[0].startStamp), 'mm-dd HH:MM') }}~{{
+            formatDate(new Date(state.selected[0].endStamp), 'mm-dd HH:MM')
+          }}
+        </span>
+        <span v-else-if="state.selected.length === 2">
+          {{ formatDate(new Date(state.selected[0].startStamp), 'mm-dd HH:MM') }}~{{
+            formatDate(new Date(state.selected[1].endStamp), 'mm-dd HH:MM')
+          }}
+        </span>
       </p>
       <p>
-        <strong><van-icon name="clock-o" :size="16" />预约时长:</strong>
+        <strong>
+          <van-icon
+            name="clock-o"
+            :size="16"
+          />
+          预约时长:
+        </strong>
         <span>{{ getAppointTime() }}</span>
       </p>
     </header>
     <div class="main">
       <template v-for="(item, index) in state.calendar">
         <h4 v-if="item.startTime == '00:00'">{{ formatDate(new Date(item.startStamp), 'YYYY-mm-dd/WWW') }}</h4>
-        <div class="tag" :class="{ disabled: item.disabled, selected: item.selected }" @click="onTagClick(item, index)">
+        <div
+          class="tag"
+          :class="{ disabled: item.disabled, selected: item.selected }"
+          @click="onTagClick(item, index)"
+        >
           {{ item.startTime }}-{{ item.endTime }}
         </div>
       </template>
     </div>
   </div>
   <van-action-bar placeholder>
-    <van-action-bar-button class="w100" type="primary" text="提交" @click="onClickButton" />
+    <van-action-bar-button
+      class="w100"
+      type="primary"
+      text="提交"
+      @click="onClickButton"
+    />
   </van-action-bar>
 </template>
 <script lang="ts" setup>
@@ -54,15 +83,15 @@
     ent_at: '23:50:00',
     currentWeekAppointList: [],
     selected: [],
-    calendar: []
+    calendar: [],
   })
   // 获取系统设置时间间隔
   const getTimeSplit = async () => {
     const [err, res]: ToResponse = await to(
       instApi.getSettingDetail({
         instId: Number(instId.value),
-        code: 'InstCfgAppoint'
-      })
+        code: 'InstCfgAppoint',
+      }),
     )
     if (err) return
     if (res.code == 200) {
@@ -78,15 +107,20 @@
     let params = {
       instId: instId.value,
       date: currentDate,
-      dateType: 'week'
+      dateType: 'week',
     }
     await Promise.all([
-      instApi.getAppointInfo({ ...params }),    
-      instApi.getAppointInfo({...params, date: nextWeek})
+      instApi.getAppointInfo({ ...params }),
+      instApi.getAppointInfo({ ...params, date: nextWeek }),
     ]).then(([now, next]) => {
       const { appoint, unavailable, furtherLimit } = now.data
       const { appoint: appointNext, unavailable: unavailableNext } = next.data
-      let allDate = [...(appoint || []), ...(unavailable || []), ...(appointNext || []), ...(unavailableNext || [])].map((item) => ({
+      let allDate = [
+        ...(appoint || []),
+        ...(unavailable || []),
+        ...(appointNext || []),
+        ...(unavailableNext || []),
+      ].map((item) => ({
         ...item,
         start: item.startTime ? item.startTime : item.start,
         startStamp: new Date(item.startTime ? item.startTime : item.start).getTime(),
@@ -123,17 +157,18 @@
         startTime: formatDate(new Date(stamp), 'HH:MM'),
         endTime: formatDate(new Date(stamp + 1000 * split * 60), 'HH:MM'),
         disabled: stamp < now,
-        selected: false
+        selected: false,
       }
       // 禁用不可预约时间段和已预约时间段
-      for(const item of state.currentWeekAppointList) {
-        if(obj.endStamp <= item.endStamp) {
+      for (const item of state.currentWeekAppointList) {
+        // 检查时间段是否重叠:两个时间段重叠的条件是开始时间小于对方结束时间且结束时间大于对方开始时间
+        if (obj.startStamp < item.endStamp && obj.endStamp > item.startStamp) {
           obj.disabled = true
-          continue
+          break // 找到重叠就可以跳出循环了
         }
       }
       // 最早预约时间
-      if(state.furtherLimit && obj.startStamp >= new Date(state.furtherLimit).getTime()) {
+      if (state.furtherLimit && obj.startStamp >= new Date(state.furtherLimit).getTime()) {
         obj.disabled = true
       }
       state.calendar.push(obj)
@@ -168,7 +203,7 @@
       if (flag) {
         showNotify({
           message: '选中时间段已被占用,请重新选择',
-          type: 'warning'
+          type: 'warning',
         })
         return
       }
@@ -189,10 +224,10 @@
   const getAppointTime = () => {
     let startDate: Date = new Date()
     let endDate: Date = new Date()
-    if(state.selected.length === 1) {
+    if (state.selected.length === 1) {
       startDate = new Date(state.selected[0].startStamp)
       endDate = new Date(state.selected[0].endStamp)
-    } else if(state.selected.length === 2) {
+    } else if (state.selected.length === 2) {
       startDate = new Date(state.selected[0].startStamp)
       endDate = new Date(state.selected[1].endStamp)
     } else {
@@ -213,16 +248,16 @@
     return `${days}天${hours}小时${minutes}分`
   }
   const onClickButton = () => {
-    if(!state.selected.length) {
+    if (!state.selected.length) {
       showNotify({
         message: '请选择预约时间',
-        type: 'warning'
+        type: 'warning',
       })
       return
     }
     let startTime = 0
     let endTime = 0
-    if(state.selected.length === 1) {
+    if (state.selected.length === 1) {
       startTime = state.selected[0].startStamp
       endTime = state.selected[0].endStamp
     } else {
@@ -234,8 +269,8 @@
       query: {
         id: instId.value,
         startTime,
-        endTime
-      }
+        endTime,
+      },
     })
   }
   onMounted(() => {

+ 154 - 114
src/view/myCashFlow/index.vue

@@ -1,7 +1,13 @@
 <template>
   <div class="entry-container">
-    <div class="search-wrap" ref="searchWrapRef">
-      <el-form :model="state.queryParams" ref="queryRef">
+    <!-- <div
+      class="search-wrap"
+      ref="searchWrapRef"
+    >
+      <el-form
+        :model="state.queryParams"
+        ref="queryRef"
+      >
         <el-form-item prop="serialNo">
           <el-select
             v-model="state.queryParams.feeType"
@@ -26,8 +32,14 @@
             clearable
             @change="search"
           >
-            <el-option label="已支付" value="20"></el-option>
-            <el-option label="未支付" value="10"></el-option>
+            <el-option
+              label="已支付"
+              value="20"
+            ></el-option>
+            <el-option
+              label="未支付"
+              value="10"
+            ></el-option>
           </el-select>
         </el-form-item>
         <el-form-item prop="serialNo">
@@ -42,9 +54,15 @@
         </el-form-item>
       </el-form>
       <div style="text-align: right">
-        <el-button @click="handleExport" style="height: 25px" type="primary">导出</el-button>
+        <el-button
+          @click="handleExport"
+          style="height: 25px"
+          type="primary"
+        >
+          导出
+        </el-button>
       </div>
-    </div>
+    </div> -->
 
     <div class="list-container">
       <van-list
@@ -53,66 +71,73 @@
         finished-text="没有更多了"
         @load="onLoad"
       >
-        <van-cell v-for="item in state.pageList" :key="item" @click="handleCheckDetail(item)">
+        <van-cell
+          v-for="item in state.pageList"
+          :key="item"
+        >
           <template #default>
             <div class="list">
               <header class="flex justify-between">
                 <strong class="title">{{ `${item.userName}的费用流水` }}</strong>
-                <van-tag v-if="item.approveStatus == ApproveStatus.WAIT_SUBMIT" type="warning"
-                  >待提交</van-tag
+                <van-tag
+                  v-if="item.approveStatus == ApproveStatus.WAIT_SUBMIT"
+                  type="warning"
                 >
-                <van-tag v-else-if="item.approveStatus == ApproveStatus.APPROVING" type="primary"
-                  >审核中</van-tag
+                  待提交
+                </van-tag>
+                <van-tag
+                  v-else-if="item.approveStatus == ApproveStatus.APPROVING"
+                  type="primary"
                 >
-                <van-tag v-else-if="item.approveStatus == ApproveStatus.PASS" type="success"
-                  >通过</van-tag
+                  审核中
+                </van-tag>
+                <van-tag
+                  v-else-if="item.approveStatus == ApproveStatus.PASS"
+                  type="success"
                 >
-                <van-tag v-else-if="item.approveStatus == ApproveStatus.REVOKE" type="success"
-                  >撤销</van-tag
+                  通过
+                </van-tag>
+                <van-tag
+                  v-else-if="item.approveStatus == ApproveStatus.REVOKE"
+                  type="success"
                 >
-                <van-tag v-else-if="item.approveStatus == ApproveStatus.REFUSE" type="danger"
-                  >拒绝</van-tag
+                  撤销
+                </van-tag>
+                <van-tag
+                  v-else-if="item.approveStatus == ApproveStatus.REFUSE"
+                  type="danger"
                 >
+                  拒绝
+                </van-tag>
               </header>
               <p class="inst-title">
                 <span>费用类型</span>
-                <span class="title ml8">{{ item.projectGroupName }}</span>
+                <span class="title ml8">{{ formatterFboType(item) }}</span>
               </p>
               <p class="inst-title">
                 <span>费用时间</span>
                 <span class="title ml8">
-                  {{ item.userName }}
+                  {{ item.fbdStartDate ? dayjs(item.fbdStartDate).format('YYYY-MM-DD') : '' }} ~
+                  {{ item.fbdEndDate ? dayjs(item.fbdEndDate).format('YYYY-MM-DD') : '' }}
                 </span>
               </p>
               <p class="inst-title">
                 <span>确认时间</span>
                 <span class="title ml8">
-                  {{ formatToChineseDate(item.createdTime) }}
+                  {{ item.confirmTime ? dayjs(item.confirmTime).format('YYYY-MM-DD') : '' }}
                 </span>
               </p>
               <p class="inst-title">
                 <span>总计金额</span>
-                <span class="title ml8">
-                  {{ formatToChineseDate(item.startDate) }}
-                </span>
+                <span class="title ml8">{{ item.fboActualAmount }} 元</span>
               </p>
               <p class="inst-title">
                 <span>状态</span>
                 <span class="title ml8">
-                  {{ item.number }}
+                  {{ formatterFboStatus(item) }}
                 </span>
               </p>
-              <footer class="flex justify-between mt16">
-                <span class="title">
-                  <el-button
-                    style="height: 25px"
-                    type="primary"
-                    @click.stop="handleRefundable(item)"
-                    >确认</el-button
-                  >
-                </span>
-                <span class="time">{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
-              </footer>
+              <footer class="flex justify-between mt16"></footer>
             </div>
           </template>
         </van-cell>
@@ -134,47 +159,42 @@
 </template>
 
 <script lang="ts" setup>
-  import { reactive, ref, onMounted, defineAsyncComponent, ComponentPublicInstance } from 'vue';
-  import to from 'await-to-js';
+  import { reactive, ref, onMounted, defineAsyncComponent, ComponentPublicInstance } from 'vue'
+  import to from 'await-to-js'
+  import dayjs from 'dayjs'
 
-  import { formatDate, formatToChineseDate } from '/@/utils/formatTime';
-  import {
-    ApproveStatus,
-    ReturnStatus,
-    LeavelList,
-    ApproveStatusList,
-    MyCageType,
-    FeeTypeList,
-  } from '/@/constants/pageConstants';
-  import { useUserInfos } from '/@/hooks/useUserInfos';
-  import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal';
-  import { useFinaceApi } from '/@/api/finace';
+  import { formatDate, formatToChineseDate } from '/@/utils/formatTime'
+  import { ApproveStatus, ApproveStatusList, MyCageType, FeeTypeList } from '/@/constants/pageConstants'
+  import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
+  import { useFinaceApi } from '/@/api/finace'
+  import { useBillApi } from '/@/api/instr/finance/bill'
+  import { useUserInfos } from '/@/hooks/useUserInfos'
 
   interface ReturnCageDialogInstance extends ComponentPublicInstance {
-    handleOpenRefundableDialog: (id: number) => void;
+    handleOpenRefundableDialog: (id: number) => void
   }
 
   interface DetailModalInstance extends ComponentPublicInstance {
-    initForm: (id: number) => void;
+    initForm: (id: number) => void
   }
 
-  const DetailModal = defineAsyncComponent(
-    () => import('/@/view/animal/application/components/Detail.vue')
-  );
+  const DetailModal = defineAsyncComponent(() => import('/@/view/animal/application/components/Detail.vue'))
   const ReturnCageDialog = defineAsyncComponent(
-    () => import('/@/view/animal/application/components/ReturnCageDialog.vue')
-  );
+    () => import('/@/view/animal/application/components/ReturnCageDialog.vue'),
+  )
 
-  const { userInfos } = useUserInfos();
-  const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi();
-  const finaceApi = useFinaceApi();
+  const { userInfos } = useUserInfos()
 
-  const returnCageDialogRef = ref<ReturnCageDialogInstance>();
-  const detailModalRef = ref<DetailModalInstance>();
+  const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi()
+  const finaceApi = useFinaceApi()
+  const billApi = useBillApi()
 
-  const showDetailDialog = ref<boolean>(false);
-  const activeStatus = ref<MyCageType>(MyCageType.MINE_CAGE);
-  const currentRefundableItemNumber = ref<number>(0);
+  const returnCageDialogRef = ref<ReturnCageDialogInstance>()
+  const detailModalRef = ref<DetailModalInstance>()
+
+  const showDetailDialog = ref<boolean>(false)
+  const activeStatus = ref<MyCageType>(MyCageType.MINE_CAGE)
+  const currentRefundableItemNumber = ref<number>(0)
 
   const state = reactive({
     pageList: [],
@@ -182,91 +202,111 @@
     finished: false,
     queryParams: {
       pageNum: 1,
-      pageSize: 25,
-      isMyself: 1,
-      feeType: '',
-      feeStatus: '',
-      feeTime: '',
+      pageSize: 10,
+      id: null,
     },
-  });
+  })
 
   const formatApproveStatus = (status: number) => {
-    return ApproveStatusList.find(item => item.id === status)?.name || '';
-  };
+    return ApproveStatusList.find((item) => item.id === status)?.name || ''
+  }
 
   const handleRefundable = (row: any) => {
-    currentRefundableItemNumber.value = row.number;
-    returnCageDialogRef.value.handleOpenRefundableDialog(row.id);
-  };
+    currentRefundableItemNumber.value = row.number
+    returnCageDialogRef.value.handleOpenRefundableDialog(row.id)
+  }
 
   const handleRefresh = () => {
-    resetQueryParams();
-    onLoad();
-  };
+    resetQueryParams()
+    onLoad()
+  }
 
   const resetQueryParams = () => {
-    (state.pageList = []),
+    ;(state.pageList = []),
       (state.loading = false),
       (state.finished = false),
       (state.queryParams = {
         pageNum: 1,
-        pageSize: 25,
-        isMyself: 1,
-        feeType: '',
-        feeStatus: '',
-        feeTime: '',
-      });
-  };
+        pageSize: 10,
+        id: null,
+      })
+  }
 
   const onLoad = async (isSearch?: boolean) => {
-    state.loading = true;
-    const apiRequest =
-      activeStatus.value === MyCageType.MINE_CAGE
-        ? platAnimalCageApplicationApi.getList(state.queryParams)
-        : platAnimalCageApplicationApi.getMyCageHistoryList(state.queryParams);
+    state.loading = true
+
+    let param = state.queryParams
+    param.id = userInfos.value.id
 
-    const [err, res]: ToResponse = await to(apiRequest);
+    const [err, res]: ToResponse = await to(billApi.getFinBillableOrderList(param))
 
-    state.loading = false;
+    state.loading = false
 
-    if (err) return;
+    if (err) return
 
-    const list = res?.data?.list || [];
+    const list = res?.data?.list || []
 
     if (!isSearch) {
       for (const item of list) {
-        state.pageList.push(item);
+        state.pageList.push(item)
       }
-      state.queryParams.pageNum++;
+      state.queryParams.pageNum++
       if (list.length < state.queryParams.pageSize) {
-        state.finished = true;
+        state.finished = true
       }
     } else {
-      state.pageList = list;
+      state.pageList = list
     }
-  };
+  }
 
   const handleCheckDetail = (row: any) => {
-    detailModalRef.value.initForm(row.id);
-    showDetailDialog.value = true;
-  };
+    detailModalRef.value.initForm(row.id)
+    showDetailDialog.value = true
+  }
 
   const search = () => {
-    onLoad(true);
-  };
+    onLoad(true)
+  }
 
-  const getList = async () => {
-    const [err, res]: ToResponse = await to(finaceApi.getList(state.queryParams));
-    if (err) return;
-    console.log(res);
-  };
+  const formatterFboType = (row: any) => {
+    if (row.fboBizCode.startsWith('WT')) {
+      return '技术服务计费'
+    } else if (row.fboBizCode.startsWith('YY')) {
+      return '仪器预约费用'
+    } else if (row.fboBizCode.startsWith('PR')) {
+      return '平台入室费用'
+    } else if (row.fboBizCode.startsWith('AM')) {
+      return '动物管理费用'
+    } else {
+      return '其他管理费用'
+    }
+  }
+
+  // 10计费中 20待确认 30已确认 40 已上账 50 已报销/结算 90 已取消
+  const formatterFboStatus = (row: any) => {
+    switch (row.fboStatus) {
+      case '10':
+        return '计费中'
+      case '20':
+        return '待确认'
+      case '30':
+        return '已确认'
+      case '40':
+        return '已上账'
+      case '50':
+        return '已报销/结算'
+      case '90':
+        return '已取消'
+      default:
+        return '-'
+    }
+  }
 
-  const handleExport = async () => {};
+  const handleExport = async () => {}
 
   onMounted(() => {
-    getList();
-    onLoad();
-  });
+    onLoad()
+  })
 </script>
 
 <style lang="scss" scoped>

+ 187 - 42
src/view/register/index.vue

@@ -10,18 +10,46 @@
   <div class="register">
     <div class="form">
       <van-row class="pl10 pr10 pt10">
-        <van-button type="success" @click="changeType('10')">注册课题组成员</van-button>
-        <van-button type="primary" @click="changeType('20')">注册课题组负责人</van-button>
+        <van-button
+          type="success"
+          @click="changeType('10')"
+        >
+          注册课题组成员
+        </van-button>
+        <van-button
+          type="primary"
+          @click="changeType('20')"
+        >
+          注册课题组负责人
+        </van-button>
       </van-row>
-      <van-steps :active="state.active" class="pl20 pr20 pt10">
+      <van-steps
+        :active="state.active"
+        class="pl20 pr20 pt10"
+      >
         <van-step>登录信息</van-step>
         <van-step>个人信息</van-step>
-        <!-- <van-step v-if="state.form.registerType == '20'">项目信息</van-step> -->
+        <van-step v-if="state.form.registerType == '20'">项目信息</van-step>
       </van-steps>
-      <van-form ref="loginInfoRef" v-show="state.active == 0" required="auto">
+      <van-form
+        ref="loginInfoRef"
+        v-show="state.active == 0"
+        required="auto"
+      >
         <van-cell-group inset>
-          <van-field v-model="state.form.userName" label="用户名" placeholder="用户名" :rules="[{ required: true, message: '请填写用户名' }]" />
-          <van-field v-model="state.form.password" type="password" label="密码" placeholder="密码" :rules="[{ required: true, message: '请填写密码' }]" />
+          <van-field
+            v-model="state.form.userName"
+            label="用户名"
+            placeholder="用户名"
+            :rules="[{ required: true, message: '请填写用户名' }]"
+          />
+          <van-field
+            v-model="state.form.password"
+            type="password"
+            label="密码"
+            placeholder="密码"
+            :rules="[{ required: true, message: '请填写密码' }]"
+          />
           <van-field
             v-model="state.form.confirmPassword"
             type="password"
@@ -31,20 +59,54 @@
           />
         </van-cell-group>
       </van-form>
-      <van-form ref="personInfoRef" v-show="state.active == 1" required="auto">
+      <van-form
+        ref="personInfoRef"
+        v-show="state.active == 1"
+        required="auto"
+      >
         <van-cell-group inset>
-          <van-field v-model="state.form.nickName" label="姓名" placeholder="姓名" :rules="[{ required: true, message: '请填写姓名' }]" />
-          <van-field label="性别" :rules="[{ required: false }]">
+          <van-field
+            v-model="state.form.nickName"
+            label="姓名"
+            placeholder="姓名"
+            :rules="[{ required: true, message: '请填写姓名' }]"
+          />
+          <van-field
+            label="性别"
+            :rules="[{ required: false }]"
+          >
             <template #input>
-              <van-radio-group v-model="state.form.sex" label="性别" placeholder="性别" direction="horizontal">
-                <van-radio v-for="item in userSexList" :name="item.dictValue" :key="item.dictValue">{{ item.dictLabel }}</van-radio>
+              <van-radio-group
+                v-model="state.form.sex"
+                label="性别"
+                placeholder="性别"
+                direction="horizontal"
+              >
+                <van-radio
+                  v-for="item in userSexList"
+                  :name="item.dictValue"
+                  :key="item.dictValue"
+                >
+                  {{ item.dictLabel }}
+                </van-radio>
               </van-radio-group>
             </template>
           </van-field>
           <van-field label="用户类型">
             <template #input>
-              <van-radio-group v-model="state.form.userType" label="用户类型" placeholder="用户类型" direction="horizontal">
-                <van-radio v-for="item in userTypeList" :name="item.dictValue" :key="item.dictValue">{{ item.dictLabel }}</van-radio>
+              <van-radio-group
+                v-model="state.form.userType"
+                label="用户类型"
+                placeholder="用户类型"
+                direction="horizontal"
+              >
+                <van-radio
+                  v-for="item in userTypeList"
+                  :name="item.dictValue"
+                  :key="item.dictValue"
+                >
+                  {{ item.dictLabel }}
+                </van-radio>
               </van-radio-group>
             </template>
           </van-field>
@@ -57,17 +119,52 @@
             @click="showDeptPicker = true"
             :rules="[{ required: true, message: '请选择组织部门' }]"
           />
-          <van-field v-model="state.form.unitName" label="单位名称" placeholder="单位名称" :rules="[{ required: true, message: '请填写单位名称' }]" />
-          <van-field v-model="state.form.phone" type="tel" label="手机号" placeholder="手机号" :rules="[{ required: true, message: '请填写手机号' }]" />
-          <van-field v-model="state.form.email" label="邮箱" placeholder="邮箱" :rules="[{ required: true, message: '请填写邮箱' }]" />
-          <van-field label="证件类型" :rules="[{ required: true, message: '请选择证件类型' }]">
+          <van-field
+            v-model="state.form.unitName"
+            label="单位名称"
+            placeholder="单位名称"
+            :rules="[{ required: true, message: '请填写单位名称' }]"
+          />
+          <van-field
+            v-model="state.form.phone"
+            type="tel"
+            label="手机号"
+            placeholder="手机号"
+            :rules="[{ required: true, message: '请填写手机号' }]"
+          />
+          <van-field
+            v-model="state.form.email"
+            label="邮箱"
+            placeholder="邮箱"
+            :rules="[{ required: true, message: '请填写邮箱' }]"
+          />
+          <van-field
+            label="证件类型"
+            :rules="[{ required: true, message: '请选择证件类型' }]"
+          >
             <template #input>
-              <van-radio-group v-model="state.form.idType" label="证件类型" placeholder="证件类型" direction="horizontal">
-                <van-radio v-for="item in userCertList" :name="item.dictValue" :key="item.id">{{ item.dictLabel }}</van-radio>
+              <van-radio-group
+                v-model="state.form.idType"
+                label="证件类型"
+                placeholder="证件类型"
+                direction="horizontal"
+              >
+                <van-radio
+                  v-for="item in userCertList"
+                  :name="item.dictValue"
+                  :key="item.id"
+                >
+                  {{ item.dictLabel }}
+                </van-radio>
               </van-radio-group>
             </template>
           </van-field>
-          <van-field v-model="state.form.idCode" label="证件号" placeholder="证件号" :rules="[{ required: true, message: '请填写证件号' }]" />
+          <van-field
+            v-model="state.form.idCode"
+            label="证件号"
+            placeholder="证件号"
+            :rules="[{ required: true, message: '请填写证件号' }]"
+          />
           <van-field
             v-model="state.form.projectGroupName"
             v-if="state.form.registerType === '20'"
@@ -97,7 +194,7 @@
           </template>
         </van-cell-group>
       </van-form>
-      <!-- <van-form ref="projectInfoRef" v-show="state.active == 2" required="auto">
+      <van-form ref="projectInfoRef" v-show="state.active == 2" required="auto">
         <van-cell-group inset>
           <van-field>
             <template #input>
@@ -107,19 +204,34 @@
                   <van-field v-model="item.projectName" placeholder="项目名称" class="mt10"></van-field>
                   <van-field v-model="item.projectTypeName" is-link readonly label="项目类型" placeholder="项目类型" @click="openPjtType(item, index)" />
                   <van-field v-model="item.projectSource" placeholder="项目来源"></van-field>
-                  <van-button type="danger" icon="el-icon-delete" @click="delProjectItem(index)"></van-button>
+                  <van-button style="height: 30px;" type="danger" icon="el-icon-delete" @click="delProjectItem(index)">删除</van-button>
                 </template>
               </div>
             </template>
           </van-field>
         </van-cell-group>
-      </van-form> -->
+      </van-form>
     </div>
     <footer>
-      <van-button v-if="state.active > 0" @click="preStep">上一步</van-button>
-      <van-button v-if="state.active < (state.form.registerType == '10' ? 1 : 1)" type="primary" class="ml10" @click="nextStep">下一步</van-button>
       <van-button
-        v-if="(state.form.registerType == '10' && state.active === 1) || (state.form.registerType == '20' && state.active === 1)"
+        v-if="state.active > 0"
+        @click="preStep"
+      >
+        上一步
+      </van-button>
+      <van-button
+        v-if="state.active < (state.form.registerType == '10' ? 1 : 2)"
+        type="primary"
+        class="ml10"
+        @click="nextStep"
+      >
+        下一步
+      </van-button>
+      <van-button
+        v-if="
+          (state.form.registerType == '10' && state.active === 1) ||
+          (state.form.registerType == '20' && state.active === 2)
+        "
         @click="onRegister"
         type="primary"
         class="ml10"
@@ -128,22 +240,49 @@
       </van-button>
     </footer>
     <!-- 部门选择器 -->
-    <van-popup v-model:show="showDeptPicker" position="bottom">
-      <van-picker :columns="deptData" :columns-field-names="{ text: 'deptName', value: 'id' }" @confirm="onDeptConfirm" @cancel="showDeptPicker = false" />
+    <van-popup
+      v-model:show="showDeptPicker"
+      position="bottom"
+    >
+      <van-picker
+        :columns="deptData"
+        :columns-field-names="{ text: 'deptName', value: 'id' }"
+        @confirm="onDeptConfirm"
+        @cancel="showDeptPicker = false"
+      />
     </van-popup>
     <!-- 课题组选择器 -->
-    <van-popup v-model:show="showPjtPicker" position="bottom">
-      <van-picker :columns="pjtList" :columns-field-names="{ text: 'pgName', value: 'id' }" @confirm="onPjtPicker" @cancel="showPjtPicker = false" />
+    <van-popup
+      v-model:show="showPjtPicker"
+      position="bottom"
+    >
+      <van-picker
+        :columns="pjtList"
+        :columns-field-names="{ text: 'pgName', value: 'id' }"
+        @confirm="onPjtPicker"
+        @cancel="showPjtPicker = false"
+      />
     </van-popup>
     <!-- 所在时间 -->
-    <van-popup v-model:show="showPjtDatePicker" position="bottom">
-      <van-picker-group title="预约日期" :tabs="['开始日期', '结束日期']" next-step-text="下一步" @confirm="onPjtDatePicker">
+    <van-popup
+      v-model:show="showPjtDatePicker"
+      position="bottom"
+    >
+      <van-picker-group
+        title="预约日期"
+        :tabs="['开始日期', '结束日期']"
+        next-step-text="下一步"
+        @confirm="onPjtDatePicker"
+      >
         <van-date-picker v-model="state.form.startDate" />
         <van-date-picker v-model="state.form.endDate" />
       </van-picker-group>
     </van-popup>
     <!-- 项目类型 -->
-    <van-popup v-model:show="showPjtTypePicker" position="bottom">
+    <van-popup
+      v-model:show="showPjtTypePicker"
+      position="bottom"
+    >
       <van-picker
         :columns="pjtTypeList"
         :columns-field-names="{ text: 'dictLabel', value: 'dictValue' }"
@@ -151,7 +290,10 @@
         @cancel="showPjtTypePicker = false"
       />
     </van-popup>
-    <van-notify v-model:show="show" type="danger">
+    <van-notify
+      v-model:show="show"
+      type="danger"
+    >
       <span>操作失败</span>
     </van-notify>
   </div>
@@ -191,7 +333,7 @@
   const state = reactive({
     active: 0,
     loading: {
-      signIn: false
+      signIn: false,
     },
     form: {
       id: 0,
@@ -221,8 +363,8 @@
       projectGroupName: '',
       projectGroupId: null,
       projectList: <any[]>[],
-      unitName: ''
-    }
+      unitName: '',
+    },
   })
   const getDicts = () => {
     Promise.all([
@@ -231,7 +373,7 @@
       dictApi.getDictDataByType('sys_user_certificate'),
       deptApi.getDeptTree(),
       proApi.getProjectGroupList({ noPage: true }),
-      dictApi.getDictDataByType('sci_pjt_level')
+      dictApi.getDictDataByType('sci_pjt_level'),
     ]).then(([type, sex, cert, dept, pjt, pjtType]) => {
       userTypeList.value = type.data.values || []
       userSexList.value = sex.data.values || []
@@ -297,14 +439,14 @@
       projectName: '',
       projectType: '',
       projectTypeName: '',
-      projectSource: ''
+      projectSource: '',
     })
   }
   const delProjectItem = (idx: number) => {
     state.form.projectList.splice(idx, 1)
   }
   const openPjtType = (row: any, idx: number) => {
-    pjtTypeIndex.value = idx;
+    pjtTypeIndex.value = idx
     showPjtTypePicker.value = true
   }
   const onPjtTypePicker = ({ selectedOptions }: { selectedOptions: any[] }) => {
@@ -332,7 +474,10 @@
     }
     showNotify({
       type: 'success',
-      message: '注册成功,即将跳转登录页'
+      message:
+        state.form.registerType === '20'
+          ? '你已经注册为课题组负责人,等待系统管理员审核通过'
+          : '你已经注册为课题组成员,等待课题组负责人审核通过',
     })
     router.push('/login')
   }

+ 123 - 62
src/view/user/index.vue

@@ -2,11 +2,21 @@
   <div class="app-container">
     <header>
       <!-- <img :src="userInfos.avatar" alt="" /> -->
-      <van-image class="mr10" width="100px" height="100px" round :src="userInfos.avatar" />
+      <van-image
+        class="mr10"
+        width="100px"
+        height="100px"
+        round
+        :src="userInfos.avatar"
+      />
       <div class="content">
         <p class="bold">
           <van-text-ellipsis :content="`${userInfos.nickName}(信用分:${userInfos.creditScore})`" />
-          <van-icon name="setting" size="20" @click="onRouterPush('/user/edit')" />
+          <van-icon
+            name="setting"
+            size="20"
+            @click="onRouterPush('/user/edit')"
+          />
         </p>
         <p class="flex justify-between">
           <van-text-ellipsis :content="userInfos.pgName" />
@@ -30,25 +40,28 @@
       <div class="card">
         <h4>经费统计</h4>
         <ul class="nav">
-          <li>
+          <!-- <li>
             <span>123,000</span>
             <p>账户余额(¥)</p>
-          </li>
+          </li> -->
           <li @click="onRouterPush('/my-cash-flow')">
-            <span>11</span>
-            <p>流水</p>
+            <span>{{ billInfo.totalFlowCount }}</span>
+            <p>费用流水</p>
           </li>
           <li>
-            <span>6</span>
-            <p>账单</p>
+            <span>{{ billInfo.currentMonthFee }}</span>
+            <p>本月支出</p>
           </li>
           <li>
-            <span>0</span>
-            <p>待支付</p>
+            <span>{{ billInfo.totalFee }}</span>
+            <p>累计支出</p>
           </li>
         </ul>
       </div>
-      <div class="card" @click="showOperatingGuidelines">
+      <div
+        class="card"
+        @click="showOperatingGuidelines"
+      >
         <h4>点击查看操作指引</h4>
       </div>
     </div>
@@ -58,31 +71,52 @@
           <button class="w100 ">订阅</button>
         </component>
       </wx-open-subscribe> -->
-      <van-button class="w100 mt10" color="#1cb4fd" plain @click="onRouterPush('/user/password')"
-        >修改密码</van-button
+      <van-button
+        class="w100 mt10"
+        color="#1cb4fd"
+        plain
+        @click="onRouterPush('/user/password')"
       >
-      <van-button class="w100 mt10" @click="signOut" type="primary">切换账号</van-button>
+        修改密码
+      </van-button>
+      <van-button
+        class="w100 mt10"
+        @click="signOut"
+        type="primary"
+      >
+        切换账号
+      </van-button>
     </footer>
 
-    <van-popup position="bottom" v-model:show="showPopup" :style="{ padding: '20px' }">
+    <van-popup
+      position="bottom"
+      v-model:show="showPopup"
+      :style="{ padding: '20px' }"
+    >
       <div class="operation-guide">
         <div class="guide-header">
           <h3>操作指引</h3>
-          <van-icon name="cross" @click="showPopup = false" />
+          <van-icon
+            name="cross"
+            @click="showPopup = false"
+          />
         </div>
-        
+
         <div class="steps-container">
-          <div 
-            v-for="(item, index) in handleStepList" 
+          <div
+            v-for="(item, index) in handleStepList"
             :key="item.step"
             class="step-item"
             :class="{ 'last-step': index === handleStepList.length - 1 }"
           >
             <div class="step-number-wrapper">
               <div class="step-number">{{ item.step }}</div>
-              <div v-if="index < handleStepList.length - 1" class="step-line"></div>
+              <div
+                v-if="index < handleStepList.length - 1"
+                class="step-line"
+              ></div>
             </div>
-            
+
             <div class="step-content">
               <h4 class="step-title">{{ item.title }}</h4>
               <p class="step-desc">{{ item.desc }}</p>
@@ -98,7 +132,7 @@
             </div>
           </div>
         </div>
-        
+
         <div class="guide-footer">
           <van-icon name="info-o" />
           <span>请按照步骤顺序完成操作</span>
@@ -109,17 +143,32 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref } from 'vue';
-  import { storeToRefs } from 'pinia';
-  import { useUserInfo } from '/@/stores/userInfo';
-  import { Local } from '/@/utils/storage';
-  import { showConfirmDialog } from 'vant';
-  import { useRouter } from 'vue-router';
-  const router = useRouter();
-  const storesUseUserInfo = useUserInfo();
-  const { userInfos, sdkConfig, openId } = storeToRefs(storesUseUserInfo);
-
-  const showPopup = ref<boolean>(false);
+  import { ref, onMounted } from 'vue'
+  import { storeToRefs } from 'pinia'
+  import to from 'await-to-js'
+  import { showConfirmDialog } from 'vant'
+  import { useRouter } from 'vue-router'
+
+  import { useUserInfo } from '/@/stores/userInfo'
+  import { Local } from '/@/utils/storage'
+  import { useBillApi } from '/@/api/instr/finance/bill'
+
+  const billApi = useBillApi()
+
+  const router = useRouter()
+  const storesUseUserInfo = useUserInfo()
+  const { userInfos, sdkConfig, openId } = storeToRefs(storesUseUserInfo)
+
+  const showPopup = ref<boolean>(false)
+  const billInfo = ref<{
+    totalFlowCount: number
+    currentMonthFee: number
+    totalFee: number
+  }>({
+    totalFlowCount: 0,
+    currentMonthFee: 0,
+    totalFee: 0,
+  })
 
   const handleStepList = ref([
     {
@@ -144,30 +193,41 @@
       desc: '由平台老师进行审核,通过后分配操作室',
       step: 4,
     },
-  ]);
+  ])
 
   const onRouterPush = (val: string) => {
-    router.push(val);
-  };
+    router.push(val)
+  }
   const signOut = () => {
     showConfirmDialog({
       message: '确认切换账号?',
     }).then(() => {
-      Local.remove('token');
-      window.location.reload();
-    });
-  };
+      Local.remove('token')
+      window.location.reload()
+    })
+  }
   const handleSubscribe = (errMsg: any, subscribeDetails: any) => {
-    console.log(errMsg, subscribeDetails);
-  };
+    console.log(errMsg, subscribeDetails)
+  }
 
   const showOperatingGuidelines = () => {
-    showPopup.value = true;
-  };
+    showPopup.value = true
+  }
 
   const handleStepAction = (item: any) => {
     // Implementation of handleStepAction
-  };
+  }
+
+  // 获取个人账单信息
+  const getBillInfo = async () => {
+    const [err, res]: ToResponse = await to(billApi.getMyOrderTotal())
+    if (err) return
+    billInfo.value = res?.data
+  }
+
+  onMounted(() => {
+    getBillInfo()
+  })
 </script>
 
 <style lang="scss" scoped>
@@ -256,6 +316,7 @@
           display: flex;
           margin: 10px 0;
           flex-wrap: wrap;
+          justify-content: space-between;
           li {
             flex: 0 0 25%;
             display: flex;
@@ -287,7 +348,7 @@
   .operation-guide {
     max-height: 70vh;
     overflow-y: auto;
-    
+
     .guide-header {
       display: flex;
       justify-content: space-between;
@@ -295,41 +356,41 @@
       margin-bottom: 20px;
       padding-bottom: 15px;
       border-bottom: 1px solid #eee;
-      
+
       h3 {
         margin: 0;
         font-size: 18px;
         font-weight: 600;
         color: #333;
       }
-      
+
       .van-icon {
         font-size: 18px;
         color: #999;
         cursor: pointer;
-        
+
         &:hover {
           color: #666;
         }
       }
     }
-    
+
     .steps-container {
       .step-item {
         display: flex;
         margin-bottom: 20px;
-        
+
         &.last-step {
           margin-bottom: 0;
         }
-        
+
         .step-number-wrapper {
           position: relative;
           margin-right: 15px;
           display: flex;
           flex-direction: column;
           align-items: center;
-          
+
           .step-number {
             width: 32px;
             height: 32px;
@@ -343,7 +404,7 @@
             font-size: 14px;
             box-shadow: 0 2px 8px rgba(28, 155, 253, 0.3);
           }
-          
+
           .step-line {
             width: 2px;
             height: 40px;
@@ -351,11 +412,11 @@
             margin-top: 8px;
           }
         }
-        
+
         .step-content {
           flex: 1;
           padding-top: 2px;
-          
+
           .step-title {
             margin: 0 0 8px 0;
             font-size: 16px;
@@ -363,14 +424,14 @@
             color: #333;
             line-height: 1.4;
           }
-          
+
           .step-desc {
             margin: 0 0 12px 0;
             font-size: 14px;
             color: #666;
             line-height: 1.5;
           }
-          
+
           .step-btn {
             border-radius: 20px;
             padding: 8px 16px;
@@ -378,7 +439,7 @@
             background: linear-gradient(135deg, #1c9bfd, #5eb7fc);
             border: none;
             box-shadow: 0 2px 8px rgba(28, 155, 253, 0.3);
-            
+
             &:active {
               transform: translateY(1px);
             }
@@ -386,7 +447,7 @@
         }
       }
     }
-    
+
     .guide-footer {
       display: flex;
       align-items: center;
@@ -395,13 +456,13 @@
       padding: 15px;
       background: #f8f9fa;
       border-radius: 8px;
-      
+
       .van-icon {
         margin-right: 6px;
         color: #1c9bfd;
         font-size: 16px;
       }
-      
+
       span {
         font-size: 13px;
         color: #666;