You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

923 lines
24 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="page-container">
<NavBar :title="t('moldOperate.moduleName')" :subTitle="t('moldOperate.subTitle')" />
<!-- 操作类型切换 -->
<view class="operate-tabs">
<view class="operate-tab" :class="{ active: query.operateType === '1' }" @click="switchOperateType('1')">{{
t('moldOperate.tabUp') }}</view>
<view class="operate-tab" :class="{ active: query.operateType === '2' }" @click="switchOperateType('2')">{{
t('moldOperate.tabDown') }}</view>
</view>
<!-- -->
<view class="search-card">
<view class="search-row">
<view class="search-input-wrap">
<text class="iconfont icon-search search-icon"></text>
<input v-model="query.remark" class="search-input" :placeholder="t('moldOperate.searchRemark')"
@confirm="handleSearch" />
</view>
<view class="search-btn" @click="handleSearch">{{ t('functionCommon.search') }}</view>
</view>
<view class="filter-row">
<picker mode="selector" :range="moldFilterOptions" range-key="name" :value="moldIndex"
@change="onMoldFilterChange">
<view class="filter-item">{{ moldFilterLabel }}</view>
</picker>
</view>
</view>
<!-- 列表区 -->
<scroll-view scroll-y class="list-scroll" :scroll-top="scrollTop" @scroll="onScroll" @scrolltolower="loadMore"
:lower-threshold="80">
<view class="list-wrap">
<view v-for="item in list" :key="item.id" class="type-card" @click="openDetail(item)">
<view class="card-header">
<view class="header-left">
<text class="type-name">{{ textValue(item.moldName) }}</text>
<text class="type-code">{{ t('moldOperate.deviceName') }}: {{ textValue(item.deviceName) }}</text>
</view>
<view class="type-chip">{{ typeLabel(item.operateType) }}</view>
</view>
<view class="card-body">
<view class="row">
<text class="label">{{ t('moldOperate.creatorName') }}</text>
<text class="value">{{ textValue(item.creatorName) }}</text>
</view>
<view class="row">
<text class="label">{{ t('moldOperate.createTime') }}</text>
<text class="value">{{ dateTimeLabel(item.createTime) }}</text>
</view>
</view>
<view class="card-actions">
<view class="action-btn edit-btn" @click.stop="openEdit(item)">
<uni-icons type="compose" size="18" color="#ffffff"></uni-icons>
</view>
<view class="action-btn delete-btn" @click.stop="removeItem(item)">
<uni-icons type="trash" size="18" color="#ffffff"></uni-icons>
</view>
</view>
</view>
<view v-if="loading && pageNo === 1" class="hint">{{ t('functionCommon.loading') }}</view>
<view v-else-if="!list.length" class="hint">{{ t('moldOperate.empty') }}</view>
<view v-else-if="loadingMore" class="hint">{{ t('functionCommon.loadingMore') }}</view>
<view v-else-if="finished" class="hint">{{ t('functionCommon.noMoreData') }}</view>
</view>
</scroll-view>
<!-- 悬浮新增按钮 -->
<view class="fab-btn" @click="openCreate">
<uni-icons type="plusempty" size="30" color="#fff"></uni-icons>
</view>
<!-- 返回顶部按钮 -->
<view v-if="showGoTop" class="go-top-btn" @click="goTop">
<uni-icons type="arrow-up" size="20" color="#1a3a5c"></uni-icons>
</view>
<!-- 新增/编辑弹框 -->
<uni-popup ref="formPopupRef" type="bottom">
<view class="form-popup">
<view class="popup-header">
<text class="popup-title">{{ formMode === 'create' ? t('moldOperate.createTitle') : t('moldOperate.editTitle')
}}</text>
<text class="popup-close" @click="closeForm">×</text>
</view>
<scroll-view scroll-y class="popup-scroll">
<view class="form-item">
<text class="form-label"><text class="required">*</text>{{ t('moldOperate.operateType') }}</text>
<view class="radio-row">
<view class="radio-item" :class="{ active: String(formData.operateType) === '1' }"
@click="changeFormOperateType('1')">{{ t('moldOperate.tabUp') }}</view>
<view class="radio-item" :class="{ active: String(formData.operateType) === '2' }"
@click="changeFormOperateType('2')">{{ t('moldOperate.tabDown') }}</view>
</view>
</view>
<view class="form-item">
<text class="form-label"><text class="required">*</text>{{ t('moldOperate.device') }}</text>
<picker mode="selector" :range="deviceOptions" range-key="label" :value="formDeviceIndex"
@change="onFormDeviceChange">
<view class="picker-view">{{ formDeviceLabel || t('moldOperate.placeholderDevice') }}</view>
</picker>
</view>
<view class="form-item">
<text class="form-label"><text class="required">*</text>{{ String(formData.operateType) === '2' ?
t('moldOperate.lowerMold') : t('moldOperate.mold') }}</text>
<view class="sub-action block" @click="openMoldPicker">{{ t('moldOperate.selectMold') }}</view>
</view>
<view v-if="!selectedMolds.length" class="item-empty">{{ t('moldOperate.noSelectedMold') }}</view>
<view v-for="(mold, index) in selectedMolds" :key="mold.id" class="selected-item">
<view>
<view class="selected-name">{{ textValue(mold.name || mold.moldName) }}</view>
<view class="selected-code">{{ textValue(mold.code || mold.moldCode) }}</view>
</view>
<text class="item-remove" @click="removeSelectedMold(index)">×</text>
</view>
<view class="form-item">
<text class="form-label">{{ t('moldOperate.remark') }}</text>
<textarea v-model="formData.remark" class="form-textarea" :placeholder="t('moldOperate.placeholderRemark')"
maxlength="200" />
</view>
</scroll-view>
<view class="popup-footer">
<view class="footer-btn cancel" @click="closeForm">{{ t('functionCommon.cancel') }}</view>
<view class="footer-btn confirm" @click="submitForm">{{ t('functionCommon.save') }}</view>
</view>
</view>
</uni-popup>
<!-- 模具选择弹框 -->
<uni-popup ref="moldPickerRef" type="bottom">
<view class="picker-popup">
<view class="picker-header">
<text class="picker-title">{{ t('moldOperate.selectMold') }}</text>
<text class="picker-close" @click="closeMoldPicker">×</text>
</view>
<scroll-view scroll-y class="picker-scroll">
<view v-for="m in pickerSourceList" :key="m.id" class="picker-item" @click="toggleMold(m)">
<view>
<view class="picker-item-name">{{ textValue(m.name || m.moldName) }}</view>
<view class="picker-item-code">{{ textValue(m.code || m.moldCode) }}</view>
</view>
<text class="picker-check">{{ selectedMoldIdSet.has(String(m.id)) ? '✓' : '' }}</text>
</view>
<view v-if="pickerLoading" class="picker-hint">{{ t('functionCommon.loading') }}</view>
<view v-else-if="!pickerSourceList.length" class="picker-hint">{{ t('moldOperate.noMoldData') }}</view>
</scroll-view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { computed, reactive, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldList, getInTransitMoldAllList } from '@/api/mes/mold'
import { getMoldOperatePage, getMoldOperateDetail, createMoldOperate, updateMoldOperate, deleteMoldOperate, getLowerMoldList, getDeviceLedgerList } from '@/api/mes/moldoperate'
const { t } = useI18n()
const list = ref([])
const loading = ref(false)
const loadingMore = ref(false)
const finished = ref(false)
const pageNo = ref(1)
const pageSize = ref(10)
const scrollTop = ref(0)
const showGoTop = ref(false)
const query = reactive({
operateType: '1',
moldId: undefined,
remark: ''
})
const moldFilterOptions = ref([{ id: undefined, name: '' }])
const moldIndex = ref(0)
const moldFilterLabel = computed(() => moldFilterOptions.value[moldIndex.value]?.name || t('moldOperate.allMold'))
const formPopupRef = ref(null)
const moldPickerRef = ref(null)
const formMode = ref('create')
const formData = reactive({
id: undefined,
operateType: '1',
deviceId: undefined,
moldId: '',
lowerMoldId: '',
remark: ''
})
const selectedMolds = ref([])
const selectedMoldIdSet = computed(() => new Set(selectedMolds.value.map((m) => String(m.id))))
const deviceOptions = ref([])
const formDeviceIndex = computed(() => {
const idx = deviceOptions.value.findIndex((d) => Number(d.value) === Number(formData.deviceId))
return idx < 0 ? 0 : idx
})
const formDeviceLabel = computed(() => deviceOptions.value.find((d) => Number(d.value) === Number(formData.deviceId))?.label || '')
const upperMoldOptions = ref([])
const lowerMoldOptions = ref([])
const pickerLoading = ref(false)
const pickerSourceList = computed(() => (String(formData.operateType) === '2' ? lowerMoldOptions.value : upperMoldOptions.value))
function textValue(v) {
if (v === 0) return '0'
if (v === null || v === undefined) return '-'
const s = String(v).trim()
return s || '-'
}
function dateTimeLabel(v) {
if (!v) return '-'
const d = new Date(Number(v))
if (Number.isNaN(d.getTime())) return textValue(v)
const p = (n) => String(n).padStart(2, '0')
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
}
function normalizePageData(res) {
const root = res && res.data !== undefined ? res.data : res
const l = root?.list || root?.rows || root?.records || root?.data?.list || []
const total = root?.total ?? root?.data?.total ?? (Array.isArray(l) ? l.length : 0)
return { list: Array.isArray(l) ? l : [], total: Number(total || 0) }
}
function typeLabel(type) {
return String(type) === '2' ? t('moldOperate.tabDown') : t('moldOperate.tabUp')
}
async function fetchList(reset = true) {
if (reset) {
pageNo.value = 1
finished.value = false
}
if (pageNo.value === 1) loading.value = true
else loadingMore.value = true
try {
const params = {
pageNo: pageNo.value,
pageSize: pageSize.value,
operateType: query.operateType,
moldId: query.moldId,
remark: query.remark || undefined
}
const res = await getMoldOperatePage(params)
const page = normalizePageData(res)
if (reset) list.value = page.list
else list.value = [...list.value, ...page.list]
finished.value = list.value.length >= page.total || page.list.length < pageSize.value
} catch (e) {
if (!reset) pageNo.value = Math.max(1, pageNo.value - 1)
uni.showToast({ title: t('functionCommon.loadFailed'), icon: 'none' })
} finally {
loading.value = false
loadingMore.value = false
}
}
async function loadMoldFilter() {
const res = await getMoldList({})
const root = res && res.data !== undefined ? res.data : res
const data = Array.isArray(root) ? root : Array.isArray(root?.list) ? root.list : []
moldFilterOptions.value = [{ id: undefined, name: t('moldOperate.allMold') }, ...data]
}
async function loadDevices() {
const res = await getDeviceLedgerList({})
const root = res && res.data !== undefined ? res.data : res
const data = Array.isArray(root) ? root : Array.isArray(root?.list) ? root.list : []
deviceOptions.value = data.map((d) => ({
value: d.id,
label: `${d.deviceCode || ''} ${d.deviceName || ''}`.trim()
}))
}
async function loadUpperMoldOptions() {
const res = await getInTransitMoldAllList()
const root = res && res.data !== undefined ? res.data : res
upperMoldOptions.value = Array.isArray(root) ? root : []
}
async function loadLowerMoldOptions() {
if (!formData.deviceId) {
lowerMoldOptions.value = []
return
}
const res = await getLowerMoldList(formData.deviceId)
const root = res && res.data !== undefined ? res.data : res
lowerMoldOptions.value = Array.isArray(root) ? root : []
}
function switchOperateType(type) {
query.operateType = type
fetchList(true)
}
function handleSearch() { fetchList(true) }
function onMoldFilterChange(e) {
const idx = Number(e?.detail?.value || 0)
moldIndex.value = idx
const option = moldFilterOptions.value[idx]
query.moldId = option ? option.id : undefined
fetchList(true)
}
function onScroll(e) {
const top = Number(e?.detail?.scrollTop || 0)
showGoTop.value = top > 600
}
function goTop() { scrollTop.value = 0 }
async function loadMore() {
if (loading.value || loadingMore.value || finished.value) return
pageNo.value += 1
await fetchList(false)
}
function resetFormData() {
formData.id = undefined
formData.operateType = query.operateType || '1'
formData.deviceId = undefined
formData.moldId = ''
formData.lowerMoldId = ''
formData.remark = ''
selectedMolds.value = []
}
function openCreate() {
formMode.value = 'create'
resetFormData()
formPopupRef.value?.open()
}
async function openEdit(row) {
if (!row?.id) {
uni.showToast({ title: t('functionCommon.noIdEdit'), icon: 'none' })
return
}
formMode.value = 'update'
resetFormData()
try {
const res = await getMoldOperateDetail(row.id)
const detail = res?.data || {}
formData.id = detail.id
formData.operateType = String(detail.operateType || '1')
formData.deviceId = detail.deviceId
formData.remark = detail.remark || ''
await refreshPickerByType()
const selectedIds = String(formData.operateType) === '2' ? parseIdList(detail.lowerMoldId) : parseIdList(detail.moldId)
selectedMolds.value = pickerSourceList.value.filter((m) => selectedIds.includes(Number(m.id)))
formPopupRef.value?.open()
} catch (e) {
uni.showToast({ title: t('moldOperate.loadEditFailed'), icon: 'none' })
}
}
function parseIdList(raw) {
if (Array.isArray(raw)) return raw.map((x) => Number(x)).filter((x) => !Number.isNaN(x))
return String(raw || '').split(',').map((x) => Number(x)).filter((x) => !Number.isNaN(x))
}
function closeForm() { formPopupRef.value?.close() }
async function changeFormOperateType(type) {
formData.operateType = type
selectedMolds.value = []
await refreshPickerByType()
}
async function refreshPickerByType() {
pickerLoading.value = true
try {
if (String(formData.operateType) === '2') {
await loadLowerMoldOptions()
} else {
await loadUpperMoldOptions()
}
} finally {
pickerLoading.value = false
}
}
async function onFormDeviceChange(e) {
const idx = Number(e?.detail?.value || 0)
const option = deviceOptions.value[idx]
formData.deviceId = option ? option.value : undefined
if (String(formData.operateType) === '2') {
selectedMolds.value = []
await refreshPickerByType()
}
}
async function openMoldPicker() {
if (!formData.deviceId) {
uni.showToast({ title: t('moldOperate.validatorDeviceRequired'), icon: 'none' })
return
}
await refreshPickerByType()
moldPickerRef.value?.open()
}
function closeMoldPicker() { moldPickerRef.value?.close() }
function toggleMold(mold) {
const id = String(mold.id)
const idx = selectedMolds.value.findIndex((i) => String(i.id) === id)
if (idx >= 0) {
selectedMolds.value.splice(idx, 1)
return
}
selectedMolds.value.push({
id: mold.id,
name: mold.name || mold.moldName,
code: mold.code || mold.moldCode
})
}
function removeSelectedMold(index) { selectedMolds.value.splice(index, 1) }
function validForm() {
if (!formData.operateType) {
uni.showToast({ title: t('moldOperate.validatorOperateTypeRequired'), icon: 'none' })
return false
}
if (!formData.deviceId) {
uni.showToast({ title: t('moldOperate.validatorDeviceRequired'), icon: 'none' })
return false
}
if (!selectedMolds.value.length) {
uni.showToast({ title: String(formData.operateType) === '2' ? t('moldOperate.validatorLowerMoldRequired') : t('moldOperate.validatorMoldRequired'), icon: 'none' })
return false
}
return true
}
async function submitForm() {
if (!validForm()) return
const selectedIds = selectedMolds.value.map((m) => m.id)
const payload = {
id: formData.id,
operateType: Number(formData.operateType),
deviceId: formData.deviceId,
remark: formData.remark || undefined
}
if (String(formData.operateType) === '2') {
const allIds = lowerMoldOptions.value.map((m) => m.id)
const selectedSet = new Set(selectedIds.map((id) => String(id)))
payload.lowerMoldId = selectedIds.join(',')
payload.moldId = allIds.filter((id) => !selectedSet.has(String(id))).join(',')
} else {
payload.moldId = selectedIds.join(',')
}
try {
if (formMode.value === 'create') {
await createMoldOperate(payload)
uni.showToast({ title: t('functionCommon.createSuccess'), icon: 'success' })
} else {
await updateMoldOperate(payload)
uni.showToast({ title: t('functionCommon.updateSuccess'), icon: 'success' })
}
closeForm()
fetchList(true)
} catch (e) {
uni.showToast({ title: t('functionCommon.saveFailed'), icon: 'none' })
}
}
function openDetail(item) {
if (!item?.id) {
uni.showToast({ title: t('functionCommon.noIdView'), icon: 'none' })
return
}
uni.navigateTo({
url: `/pages_function/pages/moldoperate/detail?id=${encodeURIComponent(String(item.id))}`
})
}
function removeItem(item) {
if (!item?.id) {
uni.showToast({ title: t('functionCommon.noIdDelete'), icon: 'none' })
return
}
uni.showModal({
title: t('functionCommon.confirmDelete'),
content: t('moldOperate.confirmDelete'),
success: async ({ confirm }) => {
if (!confirm) return
try {
await deleteMoldOperate(item.id)
uni.showToast({ title: t('functionCommon.deleteSuccess'), icon: 'success' })
fetchList(true)
} catch (e) {
uni.showToast({ title: t('functionCommon.deleteFailed'), icon: 'none' })
}
}
})
}
onShow(async () => {
await Promise.allSettled([loadMoldFilter(), loadDevices(), loadUpperMoldOptions()])
await fetchList(true)
})
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: #f0f2f5;
}
.operate-tabs {
margin: 14rpx 24rpx 0;
display: flex;
background: #fff;
border-radius: 12rpx;
padding: 8rpx;
}
.operate-tab {
flex: 1;
text-align: center;
padding: 14rpx 0;
border-radius: 8rpx;
color: #606266;
}
.operate-tab.active {
background: #1a3a5c;
color: #fff;
font-weight: 600;
}
.search-card {
margin: 14rpx 24rpx 18rpx;
padding: 20rpx;
border-radius: 16rpx;
background: #fff;
}
.search-row {
display: flex;
gap: 16rpx;
}
.search-input-wrap {
flex: 1;
display: flex;
align-items: center;
background: #f5f7fa;
border-radius: 40rpx;
padding: 0 20rpx;
}
.search-icon {
color: #909399;
}
.search-input {
flex: 1;
height: 72rpx;
margin-left: 12rpx;
}
.search-btn {
min-width: 120rpx;
height: 72rpx;
border-radius: 36rpx;
background: #1a3a5c;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
}
.filter-row {
margin-top: 16rpx;
display: flex;
gap: 14rpx;
}
.filter-item {
min-width: 220rpx;
padding: 12rpx 18rpx;
border-radius: 10rpx;
background: #f5f7fa;
color: #303133;
font-size: 24rpx;
}
.list-scroll {
height: calc(100vh - 420rpx);
}
.list-wrap {
padding: 0 24rpx 130rpx;
}
.type-card {
background: #ffffff;
border-radius: 18rpx;
padding: 24rpx;
margin-bottom: 18rpx;
box-shadow: 0 4rpx 18rpx rgba(0, 0, 0, 0.05);
}
.card-header {
display: flex;
justify-content: space-between;
gap: 10rpx;
align-items: center;
padding-bottom: 18rpx;
border-bottom: 1rpx solid #edf0f3;
}
.header-left {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.type-name {
font-size: 32rpx;
font-weight: 700;
color: #1f2d3d;
}
.type-code {
font-size: 24rpx;
color: #8a9099;
}
.type-chip {
padding: 6rpx 16rpx;
border-radius: 20rpx;
font-size: 22rpx;
color: #096dd9;
background: rgba(24, 144, 255, 0.12);
}
.card-body {
padding-top: 16rpx;
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 12rpx;
}
.label {
font-size: 26rpx;
color: #8a9099;
}
.value {
font-size: 27rpx;
color: #30363d;
max-width: 62%;
text-align: right;
}
.card-actions {
margin-top: 24rpx;
display: flex;
justify-content: flex-end;
gap: 14rpx;
}
.action-btn {
width: 60rpx;
height: 60rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
}
.edit-btn {
background: #1a3a5c;
}
.delete-btn {
background: #e34d59;
}
.hint {
text-align: center;
color: #909399;
padding: 24rpx 0;
}
.fab-btn {
position: fixed;
right: 34rpx;
bottom: 120rpx;
width: 96rpx;
height: 96rpx;
border-radius: 50%;
background: linear-gradient(135deg, #1a3a5c 0%, #2d5a87 100%);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10rpx 30rpx rgba(26, 58, 92, 0.3);
z-index: 20;
}
.go-top-btn {
position: fixed;
right: 34rpx;
bottom: 240rpx;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background: #fff;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
display: flex;
align-items: center;
justify-content: center;
}
.form-popup {
background: #fff;
border-radius: 22rpx 22rpx 0 0;
max-height: 88vh;
}
.popup-header {
height: 96rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border-bottom: 1rpx solid #f0f0f0;
}
.popup-title {
font-size: 30rpx;
font-weight: 700;
}
.popup-close {
position: absolute;
right: 28rpx;
top: 20rpx;
font-size: 44rpx;
color: #909399;
}
.popup-scroll {
max-height: calc(88vh - 180rpx);
padding: 20rpx 26rpx;
}
.form-item {
margin-bottom: 18rpx;
}
.form-label {
font-size: 24rpx;
color: #606266;
margin-bottom: 8rpx;
display: block;
}
.required {
color: #f56c6c;
margin-right: 6rpx;
}
.radio-row {
display: flex;
gap: 12rpx;
}
.radio-item {
flex: 1;
text-align: center;
padding: 18rpx 0;
border-radius: 10rpx;
background: #f5f7fa;
color: #606266;
}
.radio-item.active {
background: rgba(26, 58, 92, 0.12);
color: #1a3a5c;
font-weight: 600;
}
.picker-view {
min-height: 72rpx;
border-radius: 12rpx;
background: #f5f7fa;
padding: 0 18rpx;
display: flex;
align-items: center;
}
.sub-action {
color: #1a3a5c;
font-size: 24rpx;
}
.sub-action.block {
background: #f5f7fa;
border-radius: 10rpx;
padding: 18rpx;
text-align: center;
}
.item-empty {
padding: 20rpx;
color: #909399;
text-align: center;
}
.selected-item {
margin-top: 10rpx;
border-radius: 12rpx;
background: #f8fafc;
padding: 14rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.selected-name {
color: #303133;
font-size: 24rpx;
}
.selected-code {
color: #909399;
font-size: 22rpx;
margin-top: 4rpx;
}
.item-remove {
color: #f56c6c;
font-size: 34rpx;
}
.form-textarea {
width: 100%;
min-height: 120rpx;
border-radius: 12rpx;
background: #f5f7fa;
padding: 14rpx 18rpx;
}
.popup-footer {
height: 88rpx;
display: flex;
}
.footer-btn {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
.cancel {
background: #f5f7fa;
color: #606266;
}
.confirm {
background: #1a3a5c;
color: #fff;
}
.picker-popup {
background: #fff;
border-radius: 22rpx 22rpx 0 0;
height: 70vh;
}
.picker-header {
height: 90rpx;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.picker-title {
font-size: 28rpx;
font-weight: 700;
}
.picker-close {
position: absolute;
right: 24rpx;
top: 18rpx;
font-size: 42rpx;
}
.picker-scroll {
height: calc(70vh - 90rpx);
}
.picker-item {
margin: 0 24rpx 12rpx;
padding: 16rpx;
border-radius: 12rpx;
background: #f8fafc;
display: flex;
justify-content: space-between;
align-items: center;
}
.picker-item-name {
color: #303133;
font-size: 26rpx;
}
.picker-item-code {
color: #909399;
font-size: 22rpx;
margin-top: 6rpx;
}
.picker-check {
color: #1a3a5c;
font-size: 34rpx;
}
.picker-hint {
text-align: center;
color: #909399;
padding: 16rpx 0 22rpx;
}
</style>