| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930 |
- <template>
- <div class="allocate-popup-container">
- <van-popup
- class="allocate-popup"
- v-model:show="state.dialog.isShowDialog"
- position="right"
- :style="{ width: '100%' }"
- closeable
- :close-on-popstate="false"
- @close="onCancel"
- >
- <header class="popup-header">
- <div class="popup-title">资源分配</div>
- <div class="popup-controls">
- <div
- class="group-selector"
- @click="groupPickerVisible = true"
- >
- <span class="label">资源组</span>
- <van-field
- :model-value="groupLabel"
- readonly
- is-link
- input-align="right"
- placeholder="全部资源组"
- border
- />
- </div>
- <van-popup
- v-model:show="groupPickerVisible"
- round
- position="bottom"
- >
- <van-picker
- title="选择资源组"
- show-toolbar
- :columns="groupOptions"
- @cancel="groupPickerVisible = false"
- @confirm="onGroupConfirm"
- />
- </van-popup>
- <van-radio-group
- v-model="resClass"
- direction="horizontal"
- class="type-radio-group"
- @change="filterOutList"
- >
- <van-radio name="">全部</van-radio>
- <van-radio
- v-for="item in cellType"
- :key="item.dictValue"
- :name="item.dictValue"
- >
- {{ item.dictLabel }}
- </van-radio>
- </van-radio-group>
- </div>
- </header>
- <div class="popup-body">
- <section class="card confirm-card">
- <div class="info-banner warning">待分配队列(已确认{{ comfirmTotal }}人)</div>
- <van-empty
- v-if="!state.confirmList.length"
- image="default"
- description="暂无队列数据"
- />
- <ul v-else>
- <li
- v-for="(item, index) in state.confirmList"
- :key="index"
- :class="{ active: index === state.active }"
- @click="state.active = index"
- >
- <div class="text">
- <p>
- 分配房间:
- <span class="assign-room">{{ item.resName || '未分配' }}</span>
- </p>
- <p>
- {{ 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>
- <div class="btns">
- <button
- v-if="item.resId"
- class="link-btn danger"
- type="button"
- @click.stop="cancelAllocate(item)"
- >
- 取消
- </button>
- <button
- class="link-btn"
- type="button"
- @click.stop="onDetail(item)"
- >
- 查看
- </button>
- </div>
- </li>
- </ul>
- </section>
- <section class="card resource-card">
- <div class="info-banner warning">资源列表(共{{ state.resourceList.length }}个)</div>
- <van-empty
- v-if="!state.resourceList.length"
- image="search"
- description="暂无资源数据"
- />
- <div
- v-else
- class="resource-grid"
- >
- <div
- v-for="item in state.resourceList"
- :key="item.id"
- class="resource-item"
- :class="{ active: curSelectedResourceId === item.id }"
- @click="onSelectResource(item)"
- >
- <div class="resource-header">
- <span class="resource-name">{{ item.resName }}</span>
- <van-tag
- type="primary"
- plain
- >
- {{ getDictLabel(cellType, item.resClass) }}
- </van-tag>
- </div>
- <p class="resource-location">{{ item.resLocation }}</p>
- <div class="resource-footer">
- <span class="resource-usage">{{ item.usingSource || 0 }}/{{ item.maxNum }}</span>
- <ul class="usage-dots">
- <li
- v-for="(p, index) in item.usedList"
- :key="index"
- :class="getUsedClass(p)"
- ></li>
- </ul>
- </div>
- </div>
- </div>
- </section>
- <section class="card usage-card">
- <div class="info-banner warning">房间使用情况</div>
- <div class="usage-list">
- <ul class="position-choose">
- <li
- v-for="(p, index) in allocationSituationList"
- :key="index"
- >
- <i :class="getUsedClass(p)"></i>
- <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>
- <van-button
- v-if="!p.userObj && (p.assignStatus == '10' || p.assignStatus == '40')"
- type="primary"
- size="mini"
- plain
- @click.stop="onAllocate(p)"
- >
- 分配
- </van-button>
- </li>
- </ul>
- </div>
- </section>
- </div>
- <div class="popup-footer">
- <van-button
- type="primary"
- plain
- block
- @click="onCancel"
- >
- 取 消
- </van-button>
- <van-button
- type="primary"
- block
- @click="onSubmit('20')"
- >
- 确 定
- </van-button>
- </div>
- </van-popup>
- <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 { showToast, showConfirmDialog } from 'vant'
- import { getDictLabel } from '/@/utils/other'
- 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 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 originalResourceList = ref<any[]>([])
- 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 originalConfirmList = ref<any[]>([])
- const confirmList = ref<any[]>([])
- const belongOrgOption = ref<any[]>([])
- const userList = ref<any[]>([])
- const platformList = ref()
- const resClass = ref('')
- const groupId = ref('') // 资源组ID
- const groupPickerVisible = ref(false)
- const detailsDialog = ref()
- const curSelectedResourceId = ref(0) // 当前选中的资源ID
- const allocationSituationList = ref<any[]>([]) // 资源分配情况列表
- const groupOptions = computed(() => {
- const options = molecularGroupList.value.map((item: any) => ({
- text: item.groupName,
- value: String(item.id),
- }))
- return [{ text: '全部资源组', value: '' }, ...options]
- })
- const groupLabel = computed(() => {
- const option = groupOptions.value.find((item) => item.value === groupId.value)
- return option ? option.text : '全部资源组'
- })
- const onGroupConfirm = (value: any, detail?: any) => {
- let option: any = null
- if (value && typeof value === 'object' && 'selectedOptions' in value) {
- option = value.selectedOptions?.[0]
- } else if (detail && typeof detail === 'object' && 'selectedOptions' in detail) {
- option = detail.selectedOptions?.[0]
- } else if (Array.isArray(value)) {
- const target = value[0]
- option = groupOptions.value.find((item) => item.value === target)
- } else if (value && typeof value === 'object') {
- option = value
- }
- groupId.value = option?.value ?? ''
- groupPickerVisible.value = false
- filterOutListByGroupId(groupId.value)
- }
- const getDicts = () => {
- Promise.all([
- systemApi.getDeptTree(),
- systemApi.getUserList({ noPage: true }),
- platformApi.getAllPlatformList({ noPage: true }),
- dictApi.getDictDataByType('sys_user_type'),
- systemApi.getProjectGroupListForApp({ 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
- originalResourceList.value = JSON.parse(JSON.stringify(res?.data?.list || []))
- resourceList.value = JSON.parse(JSON.stringify(originalResourceList.value))
- 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
- originalConfirmList.value = JSON.parse(JSON.stringify(res?.data || []))
- confirmList.value = JSON.parse(JSON.stringify(originalConfirmList.value))
- 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) => String(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 resetDialogState = () => {
- confirmList.value = JSON.parse(JSON.stringify(originalConfirmList.value))
- state.confirmList = JSON.parse(JSON.stringify(confirmList.value))
- resourceList.value = JSON.parse(JSON.stringify(originalResourceList.value))
- state.resourceList = JSON.parse(JSON.stringify(resourceList.value))
- allocationSituationList.value = []
- curSelectedResourceId.value = 0
- resClass.value = ''
- groupId.value = ''
- state.active = -1
- }
- const closeDialog = () => {
- resetDialogState()
- 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) {
- showToast({ message: '请选择待分配队列人员', type: 'text' })
- return
- }
- showConfirmDialog({
- title: '提示',
- message: '是否将选中队列人员一键分配资源?',
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- })
- .then(async () => {
- const [err]: ToResponse = await to(
- platformApi.batchAssign({ appointIds: checkedUser.map((item: any) => item.id) }),
- )
- if (err) return
- showToast({ message: '一键分配成功', type: 'success' })
- getResourceList()
- getConfirmList()
- })
- .catch(() => {})
- }
- // 提交
- const onSubmit = async (type: string) => {
- const arr = confirmList.value.filter((item: any) => item.resId)
- if (!arr.length) {
- showToast({ message: '请分配资源', type: 'text' })
- 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
- showToast({ message: '操作成功', type: 'success' })
- emit('refresh')
- closeDialog()
- }
- const onDetail = (val: number) => {
- detailsDialog.value.openDialog('get', val)
- }
- // 分配
- const onAllocate = (pos: any) => {
- if (state.active < 0) {
- showToast({ message: '请先选择一个待分配人员', type: 'text' })
- return
- }
- const activeObj = state.confirmList[state.active]
- if (activeObj.resId) {
- showToast({ message: '该人员已分配房间,如需调整请先取消当前分配', type: 'text' })
- return
- }
- const room = state.resourceList.find((item: any) => item.id == curSelectedResourceId.value)
- if (!room) {
- showToast({ message: '请选择资源房间', type: 'text' })
- return
- }
- if (room.resClass != activeObj.cellSourceType) {
- showToast({ message: '请选择正确的资源', type: 'text' })
- 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) => {
- if (!obj?.resId || !obj?.location) return
- const { resId, location } = obj
- const resetRoomUsage = (list: any[]) => {
- const room = list.find((item: any) => item.id === resId)
- if (!room?.usedList) return
- const used = room.usedList.find((i: any) => i.location === location)
- if (used) used.userObj = null
- }
- resetRoomUsage(resourceList.value)
- resetRoomUsage(state.resourceList)
- if (Array.isArray(allocationSituationList.value) && allocationSituationList.value.length) {
- const usage = allocationSituationList.value.find((item: any) => item.location === location)
- if (usage) usage.userObj = null
- }
- const user = confirmList.value.find((item) => item.id === obj.id)
- if (user) {
- user.resId = null
- user.resName = null
- user.location = null
- user.replaceId = null
- }
- obj.resId = null
- obj.resName = null
- obj.location = null
- obj.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>
- .allocate-popup-container {
- .allocate-popup {
- display: flex;
- flex-direction: column;
- height: 100%;
- padding: 16px;
- box-sizing: border-box;
- }
- .popup-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- flex-wrap: wrap;
- gap: 16px;
- margin-bottom: 16px;
- .popup-title {
- font-size: 20px;
- font-weight: 600;
- color: #111827;
- }
- .popup-controls {
- display: flex;
- align-items: center;
- gap: 12px;
- flex-wrap: wrap;
- .group-selector {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 6px 12px;
- background: #ffffff;
- border-radius: 999px;
- border: 1px solid #e5e7ef;
- box-shadow: 0 2px 6px rgba(148, 163, 184, 0.15);
- cursor: pointer;
- flex: 1;
- .label {
- width: 100px;
- font-size: 13px;
- color: #4b5563;
- font-weight: 500;
- }
- :deep(.van-field) {
- padding: 0;
- min-width: 120px;
- .van-field__control {
- font-size: 13px;
- color: #1f2937;
- }
- .van-field__control--right {
- text-align: right;
- }
- .van-field__right-icon {
- color: #2563eb;
- }
- }
- }
- .type-radio-group {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 6px 10px;
- background: #eef2ff;
- border-radius: 999px;
- .van-radio__icon {
- display: none;
- }
- .van-radio__label {
- color: #374151;
- font-size: 13px;
- }
- .van-radio--horizontal {
- padding: 0 6px;
- }
- .van-radio--horizontal.van-radio--checked .van-radio__label {
- color: #2563eb;
- font-weight: 600;
- }
- }
- }
- }
- .popup-body {
- flex: 1;
- display: flex;
- flex-direction: column;
- gap: 16px;
- overflow-y: auto;
- padding-bottom: 16px;
- }
- .card {
- background: #fff;
- border-radius: 12px;
- padding: 16px;
- box-shadow: 0 6px 18px rgba(15, 23, 42, 0.08);
- display: flex;
- flex-direction: column;
- gap: 12px;
- .info-banner {
- padding: 10px 14px;
- border-radius: 8px;
- font-size: 15px;
- font-weight: 600;
- &.warning {
- background: linear-gradient(90deg, rgba(252, 211, 77, 0.2), rgba(253, 230, 138, 0.5));
- color: #b45309;
- }
- }
- }
- .confirm-card {
- ul {
- max-height: 420px;
- overflow-y: auto;
- padding-right: 4px;
- &::-webkit-scrollbar {
- width: 6px;
- }
- &::-webkit-scrollbar-thumb {
- background: rgba(156, 163, 175, 0.4);
- border-radius: 999px;
- }
- li {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- gap: 12px;
- padding: 12px;
- border: 1px solid #e5e7eb;
- border-radius: 10px;
- transition: border-color 0.2s ease, box-shadow 0.2s ease;
- cursor: pointer;
- & + li {
- margin-top: 10px;
- }
- &.active {
- border-color: #93c5fd;
- box-shadow: 0 10px 20px rgba(59, 130, 246, 0.08);
- }
- .text {
- flex: 1;
- display: flex;
- flex-direction: column;
- gap: 6px;
- p {
- margin: 0;
- color: #374151;
- font-size: 14px;
- line-height: 1.4;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .assign-room {
- color: #2563eb;
- font-weight: 600;
- }
- }
- .btns {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- justify-content: flex-end;
- .link-btn {
- padding: 6px 12px;
- border-radius: 999px;
- border: 1px solid transparent;
- background: #eff6ff;
- font-size: 13px;
- font-weight: 500;
- color: #1d4ed8;
- cursor: pointer;
- transition: all 0.2s ease;
- line-height: 1;
- &:hover {
- background: #dbeafe;
- color: #1e40af;
- box-shadow: 0 4px 12px rgba(29, 78, 216, 0.25);
- }
- &.danger {
- background: #fee2e2;
- color: #dc2626;
- &:hover {
- background: #fecaca;
- color: #b91c1c;
- box-shadow: 0 4px 12px rgba(220, 38, 38, 0.25);
- }
- }
- }
- }
- }
- }
- }
- .resource-card {
- .van-empty {
- padding: 24px 0;
- .van-empty__description {
- color: #6b7280;
- font-size: 14px;
- }
- }
- .resource-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
- gap: 12px;
- }
- .resource-item {
- background: #f9fafb;
- border-radius: 10px;
- padding: 16px;
- border: 1px solid transparent;
- display: flex;
- flex-direction: column;
- gap: 10px;
- cursor: pointer;
- transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
- &:hover {
- transform: translateY(-2px);
- box-shadow: 0 10px 20px rgba(15, 23, 42, 0.12);
- }
- &.active {
- border-color: #60a5fa;
- box-shadow: 0 8px 18px rgba(59, 130, 246, 0.16);
- }
- .resource-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 8px;
- .resource-name {
- font-size: 16px;
- font-weight: 600;
- color: #111827;
- }
- }
- .resource-location {
- color: #6b7280;
- font-size: 13px;
- }
- .resource-footer {
- display: flex;
- align-items: center;
- justify-content: space-between;
- .resource-usage {
- font-size: 14px;
- font-weight: 600;
- color: #111827;
- }
- .usage-dots {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
- row-gap: 6px;
- list-style: none;
- padding: 0;
- margin: 0;
- max-width: 120px;
- justify-content: flex-end;
- li {
- width: 10px;
- height: 10px;
- border-radius: 50%;
- background: #86efac;
- &.used {
- background: #f87171;
- }
- &.empty-allocate {
- background: linear-gradient(90deg, #86efac 0%, #86efac 50%, #2563eb 50%, #2563eb 100%);
- }
- &.leave {
- background: #fb923c;
- }
- &.leave-allocate {
- background: linear-gradient(90deg, #fb923c 0%, #fb923c 50%, #2563eb 50%, #2563eb 100%);
- }
- }
- }
- }
- }
- }
- .usage-card {
- .usage-list {
- max-height: 360px;
- overflow-y: auto;
- padding-right: 4px;
- &::-webkit-scrollbar {
- width: 6px;
- }
- &::-webkit-scrollbar-thumb {
- background: rgba(156, 163, 175, 0.4);
- border-radius: 999px;
- }
- .position-choose {
- list-style: none;
- padding: 0;
- margin: 0;
- display: flex;
- flex-direction: column;
- gap: 8px;
- li {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 8px 10px;
- border: 1px solid #e5e7eb;
- border-radius: 8px;
- background: #f9fafb;
- i {
- width: 10px;
- height: 10px;
- border-radius: 50%;
- background: #86efac;
- &.used {
- background: #f87171;
- }
- &.empty-allocate {
- background: linear-gradient(90deg, #86efac 0%, #86efac 50%, #2563eb 50%, #2563eb 100%);
- }
- &.leave {
- background: #fb923c;
- }
- &.leave-allocate {
- background: linear-gradient(90deg, #fb923c 0%, #fb923c 50%, #2563eb 50%, #2563eb 100%);
- }
- }
- .txt-flex {
- flex: 1;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- color: #374151;
- font-size: 14px;
- }
- .van-button {
- min-width: 64px;
- }
- }
- }
- }
- }
- .popup-footer {
- display: flex;
- gap: 12px;
- margin-top: 16px;
- .van-button {
- height: 44px;
- font-size: 16px;
- font-weight: 600;
- }
- }
- }
- @media (min-width: 1024px) {
- .allocate-popup-container {
- .popup-body {
- flex-direction: row;
- .confirm-card {
- flex: 0 0 300px;
- }
- .resource-card {
- flex: 1;
- }
- .usage-card {
- flex: 0 0 280px;
- }
- }
- }
- }
- </style>
|