feat:库存调拨模块
parent
723367cf43
commit
0b996ac758
@ -0,0 +1,49 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function createMaterialMove(data) {
|
||||
return request({
|
||||
url: '/admin-api/erp/stock-move/create',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getMaterialMovePage(params = {}) {
|
||||
return request({
|
||||
url: '/admin-api/erp/stock-move/page',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function submitMaterialMove(data) {
|
||||
return request({
|
||||
url: '/admin-api/erp/stock-move/submit',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function auditMaterialMove(data) {
|
||||
return request({
|
||||
url: '/admin-api/erp/stock-move/audit',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getMoveProductPage(params = {}) {
|
||||
return request({
|
||||
url: '/admin-api/erp/product/page',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getProductStockList(productId) {
|
||||
return request({
|
||||
url: '/admin-api/erp/stock-move/product-stock-list',
|
||||
method: 'get',
|
||||
params: { productId }
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,495 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<NavBar :title="pageTitle" />
|
||||
|
||||
<view class="filter-bar">
|
||||
<view class="filter-row quick-row">
|
||||
<picker :range="statusPickerLabels" :value="statusPickerIndex" @change="onStatusFilterChange">
|
||||
<view class="status-filter">
|
||||
<text :class="['status-filter-text', selectedStatus === '' ? 'placeholder' : '']">{{ currentStatusLabel }}</text>
|
||||
<uni-icons type="bottom" size="14" color="#a8adb7"></uni-icons>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="filter-row search-row">
|
||||
<view class="keyword-wrap">
|
||||
<input
|
||||
v-model="searchKeyword"
|
||||
class="keyword-input"
|
||||
type="text"
|
||||
placeholder="搜索调拨单号"
|
||||
confirm-type="search"
|
||||
@input="handleKeywordInput"
|
||||
@confirm="handleSearch"
|
||||
/>
|
||||
</view>
|
||||
<view class="icon-filter-btn" @click="resetFilters">
|
||||
<uni-icons type="refresh" size="24" color="#7b8491"></uni-icons>
|
||||
</view>
|
||||
<view class="icon-filter-btn" @click="openFilterDrawer">
|
||||
<uni-icons type="settings" size="24" color="#7b8491"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<uni-popup ref="filterPopupRef" class="move-filter-popup" type="right" background-color="transparent" :animation="false">
|
||||
<view class="filter-drawer">
|
||||
<view class="drawer-header">
|
||||
<text class="drawer-title">更多筛选</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="drawer-body">
|
||||
<view class="drawer-section">
|
||||
<view class="drawer-section-head">
|
||||
<text class="drawer-section-title">调拨信息</text>
|
||||
</view>
|
||||
<view class="drawer-grid">
|
||||
<view class="drawer-field drawer-field-wide">
|
||||
<text class="drawer-label">创建人</text>
|
||||
<view class="drawer-picker" @click="toggleCreatorPanel">
|
||||
<text :class="['drawer-picker-text', !selectedCreator ? 'placeholder' : '']">{{ selectedCreatorLabel }}</text>
|
||||
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
|
||||
</view>
|
||||
<scroll-view v-if="creatorPanelOpen" scroll-y class="drawer-option-panel">
|
||||
<view
|
||||
v-for="item in creatorOptions"
|
||||
:key="item.value"
|
||||
:class="['drawer-option-item', selectedCreator === item.value ? 'active' : '']"
|
||||
@click="selectCreator(item)"
|
||||
>
|
||||
<text class="drawer-option-text">{{ item.label }}</text>
|
||||
<text v-if="selectedCreator === item.value" class="drawer-option-check">✓</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="drawer-field drawer-field-wide">
|
||||
<text class="drawer-label">调拨时间</text>
|
||||
<view class="drawer-date">
|
||||
<uni-datetime-picker
|
||||
v-model="moveTimeFilter"
|
||||
type="daterange"
|
||||
:clear-icon="true"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="drawer-actions">
|
||||
<view class="drawer-action reset" @click="resetFilters">重置</view>
|
||||
<view class="drawer-action confirm" @click="confirmFilterDrawer">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<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="task-card">
|
||||
<view class="card-header">
|
||||
<view class="header-main">
|
||||
<view class="header-left">
|
||||
<text class="task-no">{{ textValue(item.no) }}</text>
|
||||
</view>
|
||||
<view class="header-tags">
|
||||
<text :class="['record-tag', statusClass(item.status)]">{{ statusText(item.status) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-body">
|
||||
<view class="row">
|
||||
<text class="label">{{ itemLabel }}信息</text>
|
||||
<text class="value">{{ textValue(item.productNames) }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">调拨时间</text>
|
||||
<text class="value">{{ formatDateTime(item.moveTime || item.createTime) }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">创建人</text>
|
||||
<text class="value">{{ textValue(item.creatorName || item.creator) }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">数量</text>
|
||||
<text class="value highlight">{{ textValue(item.totalCount) }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">审核人</text>
|
||||
<text class="value">{{ textValue(item.auditUserName) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="[0, 1].includes(Number(item.status))" class="card-actions">
|
||||
<view class="action-btn submit-btn" @click.stop="handleSubmitAudit(item)">提交审核</view>
|
||||
</view>
|
||||
<view v-if="Number(item.status) === 10" class="card-actions">
|
||||
<view class="action-btn approve-btn" @click.stop="handleApprove(item)">审核通过</view>
|
||||
<view class="action-btn reject-btn" @click.stop="handleReject(item)">审核驳回</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loading && pageNo === 1" class="hint">加载中...</view>
|
||||
<view v-else-if="!list.length" class="hint">暂无调拨单</view>
|
||||
<view v-else-if="loadingMore" class="hint">加载更多...</view>
|
||||
<view v-else-if="finished" class="hint">没有更多了</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view v-if="showGoTop" class="go-top-btn" @click="goTop">
|
||||
<uni-icons type="arrow-up" size="20" color="#1f4b79"></uni-icons>
|
||||
</view>
|
||||
|
||||
<view class="add-btn" @click="goAdd">
|
||||
<text class="add-icon">+</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import NavBar from '@/components/common/NavBar.vue'
|
||||
import { auditMaterialMove, getMaterialMovePage, submitMaterialMove } from '@/api/mes/materialMove'
|
||||
import { getSimpleUserList } from '@/api/mes/moldget'
|
||||
|
||||
const selectedStatus = ref('')
|
||||
const searchKeyword = ref('')
|
||||
const filterPopupRef = ref(null)
|
||||
const selectedCreator = ref(null)
|
||||
const moveTimeFilter = ref([])
|
||||
const creatorOptions = ref([])
|
||||
const creatorPanelOpen = ref(false)
|
||||
const categoryType = ref(2)
|
||||
|
||||
const categoryNameMap = {
|
||||
1: '产品',
|
||||
2: '物料',
|
||||
3: '备件'
|
||||
}
|
||||
const itemLabel = computed(() => categoryNameMap[Number(categoryType.value)] || '物料')
|
||||
const pageTitle = computed(() => itemLabel.value + '库存调拨')
|
||||
|
||||
const statusOptions = computed(() => [
|
||||
{ label: '待调拨', value: '0' },
|
||||
{ label: '待审核', value: '10' },
|
||||
{ label: '已调拨', value: '20' },
|
||||
{ label: '已驳回', value: '1' }
|
||||
])
|
||||
const statusPickerLabels = computed(() => ['全部状态', ...statusOptions.value.map((item) => item.label)])
|
||||
const statusPickerIndex = computed(() => {
|
||||
if (selectedStatus.value === '') return 0
|
||||
const idx = statusOptions.value.findIndex((item) => item.value === selectedStatus.value)
|
||||
return idx >= 0 ? idx + 1 : 0
|
||||
})
|
||||
const currentStatusLabel = computed(() => {
|
||||
if (selectedStatus.value === '') return '全部状态'
|
||||
const current = statusOptions.value.find((item) => item.value === selectedStatus.value)
|
||||
return current ? current.label : '全部状态'
|
||||
})
|
||||
const selectedCreatorLabel = computed(() => {
|
||||
if (!selectedCreator.value) return '创建人'
|
||||
const found = creatorOptions.value.find((item) => item.value === selectedCreator.value)
|
||||
return found ? found.label : '创建人'
|
||||
})
|
||||
|
||||
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)
|
||||
let searchTimer = null
|
||||
|
||||
function textValue(value) {
|
||||
if (value === 0) return '0'
|
||||
if (value === null || value === undefined) return '-'
|
||||
const text = String(value).trim()
|
||||
return text || '-'
|
||||
}
|
||||
|
||||
function formatDateTime(value) {
|
||||
if (!value) return '-'
|
||||
if (Array.isArray(value) && value.length >= 3) {
|
||||
const [year, month, day] = value
|
||||
const pad = (n) => String(n).padStart(2, '0')
|
||||
return `${year}-${pad(month)}-${pad(day)}`
|
||||
}
|
||||
const numeric = Number(value)
|
||||
const date = Number.isFinite(numeric) ? new Date(String(value).length === 10 ? numeric * 1000 : numeric) : new Date(value)
|
||||
if (Number.isNaN(date.getTime())) return String(value)
|
||||
const pad = (n) => String(n).padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
|
||||
}
|
||||
|
||||
const STATUS_MAP = {
|
||||
0: '待调拨',
|
||||
10: '待审核',
|
||||
20: '已调拨',
|
||||
1: '已驳回'
|
||||
}
|
||||
function statusText(status) {
|
||||
const num = Number(status)
|
||||
return STATUS_MAP[num] || textValue(status)
|
||||
}
|
||||
function statusClass(status) {
|
||||
const num = Number(status)
|
||||
if (num === 0) return 'text-primary'
|
||||
if (num === 10) return 'text-warning'
|
||||
if (num === 20) return 'text-success'
|
||||
if (num === 1) return 'text-danger'
|
||||
return ''
|
||||
}
|
||||
|
||||
function normalizePageData(res) {
|
||||
const root = res && res.data !== undefined ? res.data : res
|
||||
const pageResult = root?.pageResult || root?.data?.pageResult || root?.data || root || {}
|
||||
const candidateList = pageResult?.list || pageResult?.rows || pageResult?.records || root?.list || []
|
||||
const candidateTotal = pageResult?.total ?? root?.total ?? candidateList.length
|
||||
return {
|
||||
list: Array.isArray(candidateList) ? candidateList : [],
|
||||
total: Number(candidateTotal || 0)
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchList(reset) {
|
||||
if (reset) {
|
||||
pageNo.value = 1
|
||||
finished.value = false
|
||||
}
|
||||
if (pageNo.value === 1) {
|
||||
loading.value = true
|
||||
} else {
|
||||
loadingMore.value = true
|
||||
}
|
||||
try {
|
||||
const moveTimeRange = Array.isArray(moveTimeFilter.value) ? moveTimeFilter.value : []
|
||||
const params = {
|
||||
pageNo: pageNo.value,
|
||||
pageSize: pageSize.value,
|
||||
no: searchKeyword.value.trim() || undefined,
|
||||
categoryType: categoryType.value,
|
||||
statusList: selectedStatus.value !== '' ? [Number(selectedStatus.value)] : undefined,
|
||||
creator: selectedCreator.value || undefined,
|
||||
moveTime: moveTimeRange.length === 2 ? [moveTimeRange[0] + ' 00:00:00', moveTimeRange[1] + ' 23:59:59'] : undefined
|
||||
}
|
||||
const res = await getMaterialMovePage(params)
|
||||
const page = normalizePageData(res)
|
||||
list.value = reset ? page.list : [...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: '加载失败', icon: 'none' })
|
||||
} finally {
|
||||
loading.value = false
|
||||
loadingMore.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onStatusFilterChange(event) {
|
||||
const index = Number(event?.detail?.value || 0)
|
||||
selectedStatus.value = index === 0 ? '' : (statusOptions.value[index - 1]?.value || '')
|
||||
fetchList(true)
|
||||
}
|
||||
function handleSearch() {
|
||||
clearSearchTimer()
|
||||
uni.hideKeyboard()
|
||||
fetchList(true)
|
||||
}
|
||||
function handleKeywordInput() {
|
||||
clearSearchTimer()
|
||||
searchTimer = setTimeout(() => fetchList(true), 300)
|
||||
}
|
||||
function openFilterDrawer() {
|
||||
loadCreatorOptions()
|
||||
filterPopupRef.value?.open()
|
||||
}
|
||||
function confirmFilterDrawer() {
|
||||
filterPopupRef.value?.close()
|
||||
fetchList(true)
|
||||
}
|
||||
function toggleCreatorPanel() {
|
||||
creatorPanelOpen.value = !creatorPanelOpen.value
|
||||
}
|
||||
function selectCreator(item) {
|
||||
selectedCreator.value = selectedCreator.value === item.value ? null : item.value
|
||||
creatorPanelOpen.value = false
|
||||
}
|
||||
async function loadCreatorOptions() {
|
||||
if (creatorOptions.value.length) return
|
||||
try {
|
||||
const res = await getSimpleUserList()
|
||||
const data = Array.isArray(res) ? res : (Array.isArray(res?.data) ? res.data : [])
|
||||
creatorOptions.value = data.map((user) => ({
|
||||
value: user.id || user.userId,
|
||||
label: user.nickname || user.userName || user.name || String(user.id || '')
|
||||
}))
|
||||
} catch (e) {}
|
||||
}
|
||||
function resetFilters() {
|
||||
clearSearchTimer()
|
||||
searchKeyword.value = ''
|
||||
selectedStatus.value = ''
|
||||
selectedCreator.value = null
|
||||
moveTimeFilter.value = []
|
||||
fetchList(true)
|
||||
}
|
||||
async function loadMore() {
|
||||
if (loading.value || loadingMore.value || finished.value) return
|
||||
pageNo.value += 1
|
||||
await fetchList(false)
|
||||
}
|
||||
function handleSubmitAudit(item) {
|
||||
if (!item?.id) return
|
||||
uni.showModal({
|
||||
title: '确认',
|
||||
content: '确认提交审核?',
|
||||
confirmColor: '#1f4b79',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
try {
|
||||
await submitMaterialMove({ id: item.id })
|
||||
uni.showToast({ title: '提交成功', icon: 'success' })
|
||||
fetchList(true)
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '提交失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function handleApprove(item) {
|
||||
auditMove(item, 20, '确认审核通过?', '审核通过')
|
||||
}
|
||||
function handleReject(item) {
|
||||
auditMove(item, 1, '确认审核驳回?', '已驳回')
|
||||
}
|
||||
function auditMove(item, status, content, successText) {
|
||||
if (!item?.id) return
|
||||
uni.showModal({
|
||||
title: '确认',
|
||||
content,
|
||||
confirmColor: status === 20 ? '#16a34a' : '#dc2626',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
try {
|
||||
await auditMaterialMove({ id: item.id, status })
|
||||
uni.showToast({ title: successText, icon: 'success' })
|
||||
fetchList(true)
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '操作失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function onScroll(event) {
|
||||
showGoTop.value = (event?.detail?.scrollTop || 0) > 600
|
||||
}
|
||||
function goTop() {
|
||||
scrollTop.value = 0
|
||||
}
|
||||
function goAdd() {
|
||||
uni.navigateTo({ url: `/pages_function/pages/materialMove/create?categoryType=${categoryType.value}` })
|
||||
}
|
||||
function clearSearchTimer() {
|
||||
if (searchTimer) {
|
||||
clearTimeout(searchTimer)
|
||||
searchTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
const gd = getApp().globalData || {}
|
||||
categoryType.value = Number(options?.categoryType || gd._materialMoveCategoryType || 2)
|
||||
gd._materialMoveCategoryType = categoryType.value
|
||||
})
|
||||
onShow(() => {
|
||||
fetchList(true)
|
||||
})
|
||||
onUnload(() => {
|
||||
clearSearchTimer()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container { min-height: 100vh; background: #f4f5f7; }
|
||||
.filter-bar { padding: 18rpx 14rpx 20rpx; background: #f3f4f6; }
|
||||
.filter-row { display: flex; align-items: center; gap: 18rpx; }
|
||||
.search-row { margin-top: 18rpx; }
|
||||
.quick-row > picker { min-width: 0; flex: 1; }
|
||||
.keyword-wrap, .status-filter, .icon-filter-btn { height: 66rpx; border: 1rpx solid #d9dde5; background: #ffffff; box-sizing: border-box; }
|
||||
.keyword-wrap { min-width: 0; flex: 1; display: flex; align-items: center; }
|
||||
.keyword-input { width: 100%; height: 64rpx; padding: 0 20rpx; font-size: 26rpx; color: #374151; }
|
||||
.status-filter { min-width: 0; flex: 1; display: flex; align-items: center; justify-content: space-between; padding: 0 18rpx 0 26rpx; }
|
||||
.status-filter-text { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 26rpx; color: #374151; }
|
||||
.status-filter-text.placeholder { color: #a8adb7; }
|
||||
.icon-filter-btn { width: 66rpx; flex: 0 0 66rpx; display: flex; align-items: center; justify-content: center; border-color: transparent; background: transparent; }
|
||||
|
||||
::deep(.move-filter-popup.right .uni-popup__content-transition) { transform: none !important; }
|
||||
.filter-drawer { width: 630rpx; height: calc(100vh - var(--status-bar-height)); margin-top: var(--status-bar-height); background: #f5f5f7; display: flex; flex-direction: column; overflow: hidden; border-radius: 28rpx 0 0 28rpx; }
|
||||
.drawer-header { height: 104rpx; padding: 18rpx 34rpx 0; background: #ffffff; display: flex; align-items: center; box-sizing: border-box; }
|
||||
.drawer-title { color: #1f2937; font-size: 34rpx; line-height: 1.3; font-weight: 700; }
|
||||
.drawer-body { flex: 1; min-height: 0; padding-bottom: 40rpx; box-sizing: border-box; }
|
||||
.drawer-actions { height: 126rpx; padding: 18rpx 28rpx 24rpx; box-sizing: border-box; display: flex; align-items: center; background: #ffffff; box-shadow: 0 -8rpx 24rpx rgba(17, 24, 39, 0.06); }
|
||||
.drawer-action { flex: 1; height: 72rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 600; border: 2rpx solid #174b78; box-sizing: border-box; }
|
||||
.drawer-action.reset { border-radius: 12rpx 0 0 12rpx; background: #ffffff; color: #174b78; }
|
||||
.drawer-action.confirm { border-radius: 0 12rpx 12rpx 0; background: #174b78; color: #ffffff; }
|
||||
.drawer-section { margin-bottom: 18rpx; padding: 28rpx 28rpx 30rpx; border-radius: 24rpx; background: #ffffff; box-sizing: border-box; }
|
||||
.drawer-section-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; }
|
||||
.drawer-section-title { font-size: 32rpx; line-height: 1.3; color: #1f2937; font-weight: 700; }
|
||||
.drawer-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22rpx 20rpx; }
|
||||
.drawer-field { min-width: 0; }
|
||||
.drawer-field-wide { grid-column: 1 / -1; }
|
||||
.drawer-label { display: block; margin-bottom: 12rpx; font-size: 24rpx; line-height: 1.3; color: #4b5563; font-weight: 500; }
|
||||
.drawer-picker { width: 100%; min-height: 74rpx; border-radius: 8rpx; background: #f7f8fb; box-sizing: border-box; display: flex; align-items: center; justify-content: center; padding: 0 18rpx; gap: 8rpx; }
|
||||
.drawer-picker-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 26rpx; color: #111827; text-align: center; }
|
||||
.drawer-picker-text.placeholder { color: #9ca3af; }
|
||||
.drawer-option-panel { max-height: 360rpx; margin-top: 12rpx; border-radius: 12rpx; background: #f7f8fb; overflow: hidden; }
|
||||
.drawer-option-item { min-height: 72rpx; padding: 0 24rpx; display: flex; align-items: center; justify-content: space-between; border-bottom: 1rpx solid #eceff3; box-sizing: border-box; }
|
||||
.drawer-option-item:last-child { border-bottom: 0; }
|
||||
.drawer-option-item.active { background: rgba(23, 75, 120, 0.1); }
|
||||
.drawer-option-item.active .drawer-option-text { color: #174b78; font-weight: 600; }
|
||||
.drawer-option-text { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 26rpx; color: #1f2937; }
|
||||
.drawer-option-check { flex-shrink: 0; font-size: 28rpx; color: #174b78; margin-left: 16rpx; }
|
||||
.drawer-date { width: 100%; min-height: 74rpx; border-radius: 8rpx; background: #f7f8fb; box-sizing: border-box; display: flex; align-items: center; padding: 0 12rpx; }
|
||||
.drawer-date :deep(.uni-date), .drawer-date :deep(.uni-date-editor), .drawer-date :deep(.uni-date-editor--x), .drawer-date :deep(.uni-date-x) { width: 100%; }
|
||||
.drawer-date :deep(.uni-date-editor--x), .drawer-date :deep(.uni-date-x) { border: 0; padding: 0; background: transparent; }
|
||||
|
||||
.list-scroll { height: calc(100vh - 194rpx); }
|
||||
.list-wrap { padding: 0 24rpx 160rpx; }
|
||||
.task-card { position: relative; margin-top: 20rpx; padding: 28rpx; background: #fff; border-radius: 22rpx; box-shadow: 0 8rpx 28rpx rgba(15, 23, 42, 0.06); }
|
||||
.card-header { margin-bottom: 18rpx; }
|
||||
.header-main { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; }
|
||||
.header-left { min-width: 0; flex: 1; }
|
||||
.task-no { font-size: 32rpx; font-weight: 700; color: #0f172a; }
|
||||
.header-tags { display: flex; align-items: center; gap: 12rpx; flex-shrink: 0; }
|
||||
.record-tag { padding: 8rpx 18rpx; border-radius: 999rpx; font-size: 22rpx; line-height: 1; background: #e2e8f0; color: #64748b; }
|
||||
.card-body .row { display: flex; justify-content: space-between; align-items: flex-start; gap: 20rpx; margin-top: 12rpx; }
|
||||
.card-body .row:first-child { margin-top: 0; }
|
||||
.label { width: 140rpx; font-size: 25rpx; color: #94a3b8; flex-shrink: 0; }
|
||||
.value { flex: 1; text-align: right; font-size: 27rpx; color: #334155; line-height: 1.5; }
|
||||
.value.highlight { color: #1f4b79; font-weight: 600; }
|
||||
.card-actions { display: flex; gap: 16rpx; margin-top: 20rpx; padding-top: 20rpx; border-top: 1rpx solid #f0f0f0; }
|
||||
.action-btn { flex: 1; height: 64rpx; line-height: 64rpx; text-align: center; border-radius: 10rpx; font-size: 26rpx; font-weight: 500; }
|
||||
.action-btn.approve-btn { background: #dcfce7; color: #16a34a; }
|
||||
.action-btn.reject-btn { background: #fee2e2; color: #dc2626; }
|
||||
.action-btn.submit-btn { background: #dbeafe; color: #1d4ed8; }
|
||||
.text-success { color: #16a34a; }
|
||||
.text-danger { color: #dc2626; }
|
||||
.text-warning { color: #d97706; }
|
||||
.text-primary { color: #2563eb; }
|
||||
.record-tag.text-success { color: #15803d; background: #dcfce7; }
|
||||
.record-tag.text-danger { color: #dc2626; background: #fee2e2; }
|
||||
.record-tag.text-warning { color: #d97706; background: #fef3c7; }
|
||||
.record-tag.text-primary { color: #1d4ed8; background: #dbeafe; }
|
||||
.hint { padding: 36rpx 0; text-align: center; color: #94a3b8; font-size: 26rpx; }
|
||||
.go-top-btn { position: fixed; right: 28rpx; bottom: calc(160rpx + env(safe-area-inset-bottom)); width: 92rpx; height: 92rpx; border-radius: 46rpx; background: rgba(255, 255, 255, 0.96); box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.12); display: flex; align-items: center; justify-content: center; }
|
||||
.add-btn { position: fixed; right: 28rpx; bottom: calc(56rpx + env(safe-area-inset-bottom)); width: 92rpx; height: 92rpx; border-radius: 46rpx; background: #1f4b79; box-shadow: 0 14rpx 30rpx rgba(24, 63, 108, 0.24); display: flex; align-items: center; justify-content: center; }
|
||||
.add-icon { color: #ffffff; font-size: 64rpx; line-height: 1; margin-top: -4rpx; }
|
||||
</style>
|
||||
Loading…
Reference in New Issue