|
|
<template>
|
|
|
<view class="page-container">
|
|
|
<AppTitleHeader title="模具台账" subTitle="按型号、状态、编码快速筛选" :showSubTitle="true" />
|
|
|
|
|
|
<view class="filter-card">
|
|
|
<view class="filter-row">
|
|
|
<view class="filter-item" @click="openBrandPicker">
|
|
|
<text class="filter-label">型号</text>
|
|
|
<text class="filter-value" :class="{ placeholder: !selectedBrandLabel }">
|
|
|
{{ selectedBrandLabel || '全部型号' }}
|
|
|
</text>
|
|
|
<text class="filter-arrow">›</text>
|
|
|
</view>
|
|
|
<view class="filter-item" @click="openStatusPicker">
|
|
|
<text class="filter-label">状态</text>
|
|
|
<text class="filter-value" :class="{ placeholder: !selectedStatusLabel }">
|
|
|
{{ selectedStatusLabel || '全部状态' }}
|
|
|
</text>
|
|
|
<text class="filter-arrow">›</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<view class="search-row">
|
|
|
<view class="search-wrapper">
|
|
|
<view class="search-icon">
|
|
|
<text class="iconfont icon-search"></text>
|
|
|
</view>
|
|
|
<input v-model="searchKeyword" class="search-input" type="text" placeholder="请输入模具编码或名称"
|
|
|
placeholder-class="input-placeholder" @confirm="handleSearch" />
|
|
|
<view v-if="searchKeyword" class="clear-btn" @click="clearSearch">
|
|
|
<text class="clear-icon">×</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="search-btn" @click="handleSearch">
|
|
|
<text class="search-btn-text">查询</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<scroll-view scroll-y class="content-scroll" :scroll-top="scrollTop" @scroll="handleScroll"
|
|
|
@scrolltolower="loadMore" :lower-threshold="80">
|
|
|
<view class="list-wrap">
|
|
|
<view v-for="item in list" :key="item.id" class="ledger-card" @click="openDetail(item)">
|
|
|
<view class="card-header">
|
|
|
<view class="header-left">
|
|
|
<text class="name">{{ textValue(item.name) }}</text>
|
|
|
<view class="code-wrapper">
|
|
|
<text class="code">编码:{{ textValue(item.code) }}</text>
|
|
|
<CopyButton :content="item.code" />
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="header-right">
|
|
|
<view class="status-chip" :class="statusClass(item.status)">
|
|
|
{{ moldStatusText(item.status) }}
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="card-body">
|
|
|
<view class="row">
|
|
|
<text class="label">使用次数</text>
|
|
|
<text class="value">{{ textValue(item.useTime) }}</text>
|
|
|
</view>
|
|
|
<view class="row">
|
|
|
<text class="label">创建时间</text>
|
|
|
<text class="value">{{ formatDateTime(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="confirmDelete(item)">
|
|
|
<uni-icons type="trash" size="18" color="#ffffff"></uni-icons>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<view v-if="loading && pageNo === 1" class="loading-text">加载中...</view>
|
|
|
<view v-else-if="!list.length" class="empty-text">暂无模具台账数据</view>
|
|
|
<view v-else-if="loadingMore" class="loading-text">正在加载更多...</view>
|
|
|
<view v-else-if="finished" class="finished-text">没有更多数据了</view>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
|
|
|
<view v-if="showGoTop" class="go-top-btn" @click="goTop">
|
|
|
<text class="go-top-icon">↑</text>
|
|
|
</view>
|
|
|
|
|
|
<view class="add-btn" @click="openCreate">
|
|
|
<text class="add-icon">+</text>
|
|
|
</view>
|
|
|
|
|
|
<uni-popup ref="brandPickerRef" type="bottom" background-color="#fff">
|
|
|
<view class="picker-content">
|
|
|
<view class="picker-header">
|
|
|
<text class="picker-title">选择模具型号</text>
|
|
|
<view class="picker-clear" @click="resetBrand">
|
|
|
<text class="picker-clear-text">清空</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<scroll-view scroll-y class="picker-list">
|
|
|
<view v-for="option in brandOptions" :key="option.value" class="picker-item" @click="selectBrand(option)">
|
|
|
<text class="picker-text">{{ option.label }}</text>
|
|
|
<text v-if="selectedBrandId === option.value" class="picker-check">✓</text>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
</view>
|
|
|
</uni-popup>
|
|
|
|
|
|
<uni-popup ref="statusPickerRef" type="bottom" background-color="#fff">
|
|
|
<view class="picker-content">
|
|
|
<view class="picker-header">
|
|
|
<text class="picker-title">选择模具状态</text>
|
|
|
<view class="picker-clear" @click="resetStatus">
|
|
|
<text class="picker-clear-text">清空</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="picker-list">
|
|
|
<view v-for="option in statusOptions" :key="option.value" class="picker-item" @click="selectStatus(option)">
|
|
|
<text class="picker-text">{{ option.label }}</text>
|
|
|
<text v-if="selectedStatus === option.value" class="picker-check">✓</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</uni-popup>
|
|
|
|
|
|
<uni-popup ref="formPopupRef" type="center" background-color="#fff">
|
|
|
<view class="popup-content-center">
|
|
|
<view class="popup-header">
|
|
|
<text class="picker-title">{{ formMode === 'create' ? '新增模具台账' : '编辑模具台账' }}</text>
|
|
|
</view>
|
|
|
<scroll-view scroll-y class="form-scroll">
|
|
|
<view class="form-content">
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">模具型号 <text class="required-star">*</text></text>
|
|
|
<view class="form-picker" @click="openFormBrandPicker">
|
|
|
<text class="form-picker-text" :class="{ placeholder: !formBrandLabel }">
|
|
|
{{ formBrandLabel || '请选择模具型号' }}
|
|
|
</text>
|
|
|
<text class="filter-arrow">›</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<view class="form-label-row">
|
|
|
<text class="form-label">
|
|
|
模具编码
|
|
|
<text v-if="!formData.isCode" class="required-star">*</text>
|
|
|
</text>
|
|
|
<view class="switch-wrap">
|
|
|
<text class="switch-label">自动生成</text>
|
|
|
<switch :checked="formData.isCode" color="#2d5a87"
|
|
|
@change="(e) => formData.isCode = e.detail.value" />
|
|
|
</view>
|
|
|
</view>
|
|
|
<input v-model="formData.code" :disabled="formData.isCode || formMode === 'update'" class="form-input" type="text"
|
|
|
placeholder="请输入模具编码" />
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">模具名称 <text class="required-star">*</text></text>
|
|
|
<input v-model="formData.name" class="form-input" type="text" placeholder="请输入模具名称" />
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">单位 <text class="required-star">*</text></text>
|
|
|
<view class="form-picker" @click="openUnitPicker">
|
|
|
<text class="form-picker-text" :class="{ placeholder: !formUnitLabel }">
|
|
|
{{ formUnitLabel || '请选择单位' }}
|
|
|
</text>
|
|
|
<text class="filter-arrow">›</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">入库时间 <text class="required-star">*</text></text>
|
|
|
<picker mode="date" :value="formData.inTimeDate" @change="handleInTimeChange">
|
|
|
<view class="form-picker">
|
|
|
<text class="form-picker-text" :class="{ placeholder: !formData.inTimeDate }">
|
|
|
{{ formData.inTimeDate || '请选择入库时间' }}
|
|
|
</text>
|
|
|
<text class="filter-arrow">›</text>
|
|
|
</view>
|
|
|
</picker>
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">状态</text>
|
|
|
<picker mode="selector" :range="formStatusRange" :value="formStatusIndex"
|
|
|
@change="handleFormStatusChange">
|
|
|
<view class="form-picker">
|
|
|
<text class="form-picker-text" :class="{ placeholder: formData.status === '' }">
|
|
|
{{ formStatusLabel || '请选择状态' }}
|
|
|
</text>
|
|
|
<text class="filter-arrow">›</text>
|
|
|
</view>
|
|
|
</picker>
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">使用次数</text>
|
|
|
<input v-model="formData.useTime" class="form-input" type="number" placeholder="请输入使用次数" />
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">模具图片</text>
|
|
|
<view class="image-list">
|
|
|
<view v-for="(img, idx) in formData.images" :key="`${img}-${idx}`" class="image-item">
|
|
|
<image class="image-preview" :src="img" mode="aspectFill"
|
|
|
@click="previewImages(formData.images, img)" />
|
|
|
<view class="image-delete" @click.stop="removeImage(idx)">
|
|
|
<text class="image-delete-text">×</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view v-if="formData.images.length < 6" class="image-add" @click="chooseAndUploadImages">
|
|
|
<text class="image-add-text">+</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">是否启用 <text class="required-star">*</text></text>
|
|
|
<view class="enable-group">
|
|
|
<view class="enable-item" :class="{ active: formData.isEnable === true }"
|
|
|
@click="formData.isEnable = true">
|
|
|
<text>是</text>
|
|
|
</view>
|
|
|
<view class="enable-item" :class="{ active: formData.isEnable === false }"
|
|
|
@click="formData.isEnable = false">
|
|
|
<text>否</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">备注</text>
|
|
|
<textarea v-model="formData.remark" class="form-textarea" placeholder="请输入备注" :maxlength="200" />
|
|
|
</view>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
<view class="form-footer">
|
|
|
<view class="footer-btn cancel-btn" @click="closeForm">
|
|
|
<text class="btn-text cancel-text">取消</text>
|
|
|
</view>
|
|
|
<view class="footer-btn confirm-btn" @click="submitForm">
|
|
|
<text class="btn-text">保存</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</uni-popup>
|
|
|
|
|
|
<uni-popup ref="formBrandPickerRef" type="bottom" background-color="#fff">
|
|
|
<view class="picker-content">
|
|
|
<view class="picker-header">
|
|
|
<text class="picker-title">选择模具型号</text>
|
|
|
</view>
|
|
|
<scroll-view scroll-y class="picker-list">
|
|
|
<view v-for="option in brandOptions" :key="`f-${option.value}`" class="picker-item"
|
|
|
@click="selectFormBrand(option)">
|
|
|
<text class="picker-text">{{ option.label }}</text>
|
|
|
<text v-if="formData.brandId === option.value" class="picker-check">✓</text>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
</view>
|
|
|
</uni-popup>
|
|
|
|
|
|
<uni-popup ref="unitPickerRef" type="bottom" background-color="#fff">
|
|
|
<view class="picker-content">
|
|
|
<view class="picker-header">
|
|
|
<text class="picker-title">选择单位</text>
|
|
|
</view>
|
|
|
<scroll-view scroll-y class="picker-list">
|
|
|
<view v-for="option in unitOptions" :key="`u-${option.value}`" class="picker-item"
|
|
|
@click="selectUnit(option)">
|
|
|
<text class="picker-text">{{ option.label }}</text>
|
|
|
<text v-if="Number(formData.unitId) === Number(option.value)" class="picker-check">✓</text>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
</view>
|
|
|
</uni-popup>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { ref, computed } from 'vue'
|
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
|
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
|
|
|
import CopyButton from '@/components/common/CopyButton.vue'
|
|
|
import { createMold, deleteMold, getMoldBrandTree, getMoldDetail, getMoldPage, updateMold, uploadMoldImage } from '@/api/mes/mold'
|
|
|
import { getProductUnitSimpleList, getUnitList } from '@/api/mes/product'
|
|
|
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'
|
|
|
|
|
|
const brandPickerRef = ref(null)
|
|
|
const statusPickerRef = ref(null)
|
|
|
const formPopupRef = ref(null)
|
|
|
const formBrandPickerRef = ref(null)
|
|
|
const unitPickerRef = ref(null)
|
|
|
|
|
|
const list = ref([])
|
|
|
const loading = ref(false)
|
|
|
const loadingMore = ref(false)
|
|
|
const finished = ref(false)
|
|
|
const pageNo = ref(1)
|
|
|
const pageSize = ref(10)
|
|
|
const total = ref(0)
|
|
|
|
|
|
const searchKeyword = ref('')
|
|
|
const selectedBrandId = ref(undefined)
|
|
|
const selectedStatus = ref(undefined)
|
|
|
const brandOptions = ref([{ label: '全部', value: undefined }])
|
|
|
|
|
|
const scrollTop = ref(0)
|
|
|
const showGoTop = ref(false)
|
|
|
const formMode = ref('create')
|
|
|
const formData = ref({
|
|
|
id: undefined,
|
|
|
code: '',
|
|
|
isCode: true,
|
|
|
name: '',
|
|
|
unitId: '',
|
|
|
useTime: '',
|
|
|
inTimeDate: '',
|
|
|
status: '',
|
|
|
remark: '',
|
|
|
images: [],
|
|
|
brandId: undefined,
|
|
|
isEnable: true
|
|
|
})
|
|
|
const unitOptions = ref([])
|
|
|
|
|
|
const selectedBrandLabel = computed(() => {
|
|
|
const item = brandOptions.value.find((v) => v.value === selectedBrandId.value)
|
|
|
return item ? item.label : ''
|
|
|
})
|
|
|
|
|
|
const statusOptions = computed(() => {
|
|
|
const options = []
|
|
|
for (let i = 0; i <= 9; i += 1) {
|
|
|
const label = getDictLabel(DICT_TYPE.ERP_MOLD_STATUS, i, '')
|
|
|
if (label && label !== String(i)) {
|
|
|
options.push({ label, value: i })
|
|
|
}
|
|
|
}
|
|
|
return options
|
|
|
})
|
|
|
|
|
|
const selectedStatusLabel = computed(() => {
|
|
|
const item = statusOptions.value.find((v) => v.value === selectedStatus.value)
|
|
|
return item ? item.label : ''
|
|
|
})
|
|
|
|
|
|
const formStatusLabel = computed(() => {
|
|
|
if (formData.value.status === '' || formData.value.status === undefined || formData.value.status === null) return ''
|
|
|
return moldStatusText(formData.value.status)
|
|
|
})
|
|
|
const formStatusRange = computed(() => statusOptions.value.map((v) => v.label))
|
|
|
const formStatusIndex = computed(() => {
|
|
|
const idx = statusOptions.value.findIndex((v) => Number(v.value) === Number(formData.value.status))
|
|
|
return idx >= 0 ? idx : 0
|
|
|
})
|
|
|
|
|
|
const formBrandLabel = computed(() => {
|
|
|
const item = brandOptions.value.find((v) => v.value === formData.value.brandId)
|
|
|
return item ? item.label : ''
|
|
|
})
|
|
|
|
|
|
const formUnitLabel = computed(() => {
|
|
|
const item = unitOptions.value.find((v) => Number(v.value) === Number(formData.value.unitId))
|
|
|
return item ? item.label : ''
|
|
|
})
|
|
|
|
|
|
onLoad(async () => {
|
|
|
await initAllDict()
|
|
|
await loadUnitOptions()
|
|
|
await loadBrandTree()
|
|
|
await fetchList(true)
|
|
|
})
|
|
|
|
|
|
async function loadUnitOptions() {
|
|
|
try {
|
|
|
const res = await getProductUnitSimpleList()
|
|
|
const rows = normalizeListData(res)
|
|
|
unitOptions.value = rows
|
|
|
.map((item) => ({ label: textValue(item?.name), value: item?.id }))
|
|
|
.filter((item) => item.value !== undefined && item.value !== null)
|
|
|
} catch (e) {
|
|
|
try {
|
|
|
const fallbackRes = await getUnitList()
|
|
|
const rows = normalizeListData(fallbackRes)
|
|
|
unitOptions.value = rows
|
|
|
.map((item) => ({ label: textValue(item?.name), value: item?.id }))
|
|
|
.filter((item) => item.value !== undefined && item.value !== null)
|
|
|
} catch (error) {
|
|
|
unitOptions.value = []
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function loadBrandTree() {
|
|
|
try {
|
|
|
const res = await getMoldBrandTree()
|
|
|
const tree = normalizeListData(res)
|
|
|
const flatList = [{ label: '全部', value: undefined }, ...flattenTree(tree)]
|
|
|
brandOptions.value = flatList
|
|
|
} catch (e) {
|
|
|
brandOptions.value = [{ label: '全部', value: undefined }]
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function flattenTree(nodes, level = 0) {
|
|
|
const result = []
|
|
|
const list = Array.isArray(nodes) ? nodes : []
|
|
|
for (const node of list) {
|
|
|
const id = node?.id
|
|
|
if (id === undefined || id === null) continue
|
|
|
const prefix = level > 0 ? `${' '.repeat(level)}└ ` : ''
|
|
|
result.push({
|
|
|
label: `${prefix}${textValue(node?.name)}`,
|
|
|
value: id
|
|
|
})
|
|
|
if (Array.isArray(node?.children) && node.children.length) {
|
|
|
result.push(...flattenTree(node.children, level + 1))
|
|
|
}
|
|
|
}
|
|
|
return result
|
|
|
}
|
|
|
|
|
|
async function handleSearch() {
|
|
|
await fetchList(true)
|
|
|
}
|
|
|
|
|
|
async function clearSearch() {
|
|
|
searchKeyword.value = ''
|
|
|
await fetchList(true)
|
|
|
}
|
|
|
|
|
|
function openBrandPicker() {
|
|
|
brandPickerRef.value?.open()
|
|
|
}
|
|
|
|
|
|
function openStatusPicker() {
|
|
|
statusPickerRef.value?.open()
|
|
|
}
|
|
|
|
|
|
function openFormBrandPicker() {
|
|
|
formBrandPickerRef.value?.open()
|
|
|
}
|
|
|
|
|
|
function closeFormBrandPicker() {
|
|
|
formBrandPickerRef.value?.close()
|
|
|
}
|
|
|
|
|
|
function openUnitPicker() {
|
|
|
unitPickerRef.value?.open()
|
|
|
}
|
|
|
|
|
|
function closeUnitPicker() {
|
|
|
unitPickerRef.value?.close()
|
|
|
}
|
|
|
|
|
|
async function selectBrand(option) {
|
|
|
selectedBrandId.value = option.value
|
|
|
brandPickerRef.value?.close()
|
|
|
await fetchList(true)
|
|
|
}
|
|
|
|
|
|
async function selectStatus(option) {
|
|
|
selectedStatus.value = option.value
|
|
|
statusPickerRef.value?.close()
|
|
|
await fetchList(true)
|
|
|
}
|
|
|
|
|
|
async function resetBrand() {
|
|
|
selectedBrandId.value = undefined
|
|
|
brandPickerRef.value?.close()
|
|
|
await fetchList(true)
|
|
|
}
|
|
|
|
|
|
async function resetStatus() {
|
|
|
selectedStatus.value = undefined
|
|
|
statusPickerRef.value?.close()
|
|
|
await fetchList(true)
|
|
|
}
|
|
|
|
|
|
function selectFormBrand(option) {
|
|
|
formData.value.brandId = option.value
|
|
|
formBrandPickerRef.value?.close()
|
|
|
}
|
|
|
|
|
|
function selectUnit(option) {
|
|
|
formData.value.unitId = option.value
|
|
|
unitPickerRef.value?.close()
|
|
|
}
|
|
|
|
|
|
async function loadMore() {
|
|
|
if (loading.value || loadingMore.value || finished.value) return
|
|
|
pageNo.value += 1
|
|
|
await fetchList(false)
|
|
|
}
|
|
|
|
|
|
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 keyword = searchKeyword.value.trim()
|
|
|
const params = {
|
|
|
pageNo: pageNo.value,
|
|
|
pageSize: pageSize.value,
|
|
|
code: keyword || undefined,
|
|
|
name: keyword || undefined,
|
|
|
brandId: selectedBrandId.value,
|
|
|
status: selectedStatus.value
|
|
|
}
|
|
|
const res = await getMoldPage(params)
|
|
|
const page = normalizePageData(res)
|
|
|
total.value = page.total
|
|
|
if (reset) {
|
|
|
list.value = page.list
|
|
|
} else {
|
|
|
list.value = [...list.value, ...page.list]
|
|
|
}
|
|
|
finished.value = list.value.length >= total.value || 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 handleScroll(e) {
|
|
|
const top = e?.detail?.scrollTop || 0
|
|
|
showGoTop.value = top > 600
|
|
|
}
|
|
|
|
|
|
function goTop() {
|
|
|
scrollTop.value = 0
|
|
|
}
|
|
|
|
|
|
function openDetail(item) {
|
|
|
const id = item?.id
|
|
|
if (id === undefined || id === null) {
|
|
|
uni.showToast({ title: '缺少ID,无法查看详情', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
uni.navigateTo({
|
|
|
url: `/pages_function/pages/moldLedger/detail?id=${encodeURIComponent(String(id))}`
|
|
|
})
|
|
|
}
|
|
|
|
|
|
function openCreate() {
|
|
|
formMode.value = 'create'
|
|
|
resetForm()
|
|
|
if (selectedBrandId.value !== undefined) {
|
|
|
formData.value.brandId = selectedBrandId.value
|
|
|
}
|
|
|
formPopupRef.value?.open()
|
|
|
}
|
|
|
|
|
|
async function openEdit(item) {
|
|
|
const id = item?.id
|
|
|
if (id === undefined || id === null) {
|
|
|
uni.showToast({ title: '缺少ID,无法编辑', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
try {
|
|
|
const res = await getMoldDetail(id)
|
|
|
const detail = normalizeDetailData(res)
|
|
|
formMode.value = 'update'
|
|
|
formData.value = {
|
|
|
id: detail?.id,
|
|
|
code: textValueForInput(detail?.code),
|
|
|
isCode: Boolean(detail?.isCode),
|
|
|
name: textValueForInput(detail?.name),
|
|
|
unitId: textValueForInput(detail?.unitId),
|
|
|
useTime: textValueForInput(detail?.useTime),
|
|
|
inTimeDate: normalizeDateInput(detail?.inTime),
|
|
|
status: detail?.status ?? '',
|
|
|
remark: textValueForInput(detail?.remark),
|
|
|
images: parseImages(detail?.images),
|
|
|
brandId: detail?.brandId,
|
|
|
isEnable: detail?.isEnable ?? true
|
|
|
}
|
|
|
formPopupRef.value?.open()
|
|
|
} catch (e) {
|
|
|
uni.showToast({ title: '加载编辑数据失败', icon: 'none' })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function closeForm() {
|
|
|
formPopupRef.value?.close()
|
|
|
}
|
|
|
|
|
|
async function submitForm() {
|
|
|
if (!formData.value.brandId && formData.value.brandId !== 0) {
|
|
|
uni.showToast({ title: '请选择模具型号', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
if (!formData.value.isCode && !String(formData.value.code || '').trim()) {
|
|
|
uni.showToast({ title: '模具编码必填', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
if (!String(formData.value.name || '').trim()) {
|
|
|
uni.showToast({ title: '模具名称必填', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
if (!String(formData.value.unitId || '').trim()) {
|
|
|
uni.showToast({ title: '单位ID必填', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
if (!String(formData.value.inTimeDate || '').trim()) {
|
|
|
uni.showToast({ title: '入库时间必填', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
if (formData.value.isEnable === undefined || formData.value.isEnable === null) {
|
|
|
uni.showToast({ title: '请选择是否启用', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
const payload = {
|
|
|
id: formMode.value === 'update' ? formData.value.id : undefined,
|
|
|
code: formData.value.isCode ? undefined : String(formData.value.code || '').trim(),
|
|
|
name: String(formData.value.name || '').trim(),
|
|
|
unitId: toNumberOrUndefined(formData.value.unitId),
|
|
|
brandId: formData.value.brandId,
|
|
|
useTime: toNumberOrUndefined(formData.value.useTime) ?? 0,
|
|
|
inTime: normalizeInTimeForSubmit(formData.value.inTimeDate),
|
|
|
status: toNumberOrUndefined(formData.value.status) ?? 3,
|
|
|
remark: String(formData.value.remark || '').trim() || undefined,
|
|
|
images: formData.value.images.join(','),
|
|
|
isEnable: formData.value.isEnable,
|
|
|
isCode: formData.value.isCode
|
|
|
}
|
|
|
try {
|
|
|
if (formMode.value === 'create') {
|
|
|
await createMold(payload)
|
|
|
uni.showToast({ title: '新增成功', icon: 'success' })
|
|
|
} else {
|
|
|
await updateMold(payload)
|
|
|
uni.showToast({ title: '更新成功', icon: 'success' })
|
|
|
}
|
|
|
closeForm()
|
|
|
await fetchList(true)
|
|
|
} catch (e) {
|
|
|
uni.showToast({ title: '保存失败', icon: 'none' })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function confirmDelete(item) {
|
|
|
const id = item?.id
|
|
|
if (id === undefined || id === null) {
|
|
|
uni.showToast({ title: '缺少ID,无法删除', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
uni.showModal({
|
|
|
title: '确认删除',
|
|
|
content: `确认删除模具"${textValue(item?.name)}"吗?`,
|
|
|
success: async (res) => {
|
|
|
if (!res.confirm) return
|
|
|
try {
|
|
|
await deleteMold(id)
|
|
|
uni.showToast({ title: '删除成功', icon: 'success' })
|
|
|
await fetchList(true)
|
|
|
} catch (e) {
|
|
|
uni.showToast({ title: '删除失败', icon: 'none' })
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
|
|
|
function resetForm() {
|
|
|
formData.value = {
|
|
|
id: undefined,
|
|
|
code: '',
|
|
|
isCode: true,
|
|
|
name: '',
|
|
|
unitId: '',
|
|
|
useTime: '',
|
|
|
inTimeDate: '',
|
|
|
status: '',
|
|
|
remark: '',
|
|
|
images: [],
|
|
|
brandId: undefined,
|
|
|
isEnable: true
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleInTimeChange(e) {
|
|
|
formData.value.inTimeDate = e?.detail?.value || ''
|
|
|
}
|
|
|
|
|
|
function handleFormStatusChange(e) {
|
|
|
const idx = Number(e?.detail?.value)
|
|
|
const option = statusOptions.value[idx]
|
|
|
formData.value.status = option ? option.value : ''
|
|
|
}
|
|
|
|
|
|
function normalizePageData(res) {
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
const candidateList = root?.list || root?.rows || root?.records || root?.data?.list || root?.data?.rows || []
|
|
|
const candidateTotal = root?.total ?? root?.data?.total ?? (Array.isArray(candidateList) ? candidateList.length : 0)
|
|
|
return {
|
|
|
list: Array.isArray(candidateList) ? candidateList : [],
|
|
|
total: Number(candidateTotal || 0)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function normalizeListData(res) {
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
if (Array.isArray(root)) return root
|
|
|
if (Array.isArray(root?.data)) return root.data
|
|
|
if (Array.isArray(root?.list)) return root.list
|
|
|
return []
|
|
|
}
|
|
|
|
|
|
function normalizeDetailData(res) {
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
if (root?.data && typeof root.data === 'object') return root.data
|
|
|
if (root && typeof root === 'object') return root
|
|
|
return {}
|
|
|
}
|
|
|
|
|
|
function moldStatusText(status) {
|
|
|
return getDictLabel(DICT_TYPE.ERP_MOLD_STATUS, status, textValue(status))
|
|
|
}
|
|
|
|
|
|
function statusClass(status) {
|
|
|
const label = moldStatusText(status)
|
|
|
if (label.includes('正常') || label.toLowerCase() === 'ok') return 'status-normal'
|
|
|
if (label.includes('停') || label.includes('坏') || label.includes('禁') || label.toLowerCase() === 'ng') return 'status-danger'
|
|
|
return 'status-warning'
|
|
|
}
|
|
|
|
|
|
function textValue(value) {
|
|
|
if (value === 0) return '0'
|
|
|
if (value === false) return '否'
|
|
|
if (value === true) return '是'
|
|
|
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 [y, m, d, hh = 0, mm = 0, ss = 0] = value
|
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
|
return `${y}-${pad(m)}-${pad(d)} ${pad(hh)}:${pad(mm)}:${pad(ss)}`
|
|
|
}
|
|
|
const text = String(value).trim()
|
|
|
if (!text) return '-'
|
|
|
const num = Number(text)
|
|
|
if (Number.isFinite(num)) {
|
|
|
const time = text.length === 10 ? num * 1000 : num
|
|
|
const date = new Date(time)
|
|
|
if (!Number.isNaN(date.getTime())) {
|
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`
|
|
|
}
|
|
|
}
|
|
|
const date = new Date(text)
|
|
|
if (!Number.isNaN(date.getTime())) {
|
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`
|
|
|
}
|
|
|
return text
|
|
|
}
|
|
|
|
|
|
function normalizeDateInput(value) {
|
|
|
const text = formatDateTime(value)
|
|
|
return text === '-' ? '' : text.split(' ')[0]
|
|
|
}
|
|
|
|
|
|
function toNumberOrUndefined(value) {
|
|
|
if (value === null || value === undefined || String(value).trim() === '') return undefined
|
|
|
const num = Number(value)
|
|
|
return Number.isFinite(num) ? num : undefined
|
|
|
}
|
|
|
|
|
|
function textValueForInput(value) {
|
|
|
if (value === null || value === undefined) return ''
|
|
|
return String(value)
|
|
|
}
|
|
|
|
|
|
function normalizeInTimeForSubmit(value) {
|
|
|
const text = String(value || '').trim()
|
|
|
if (!text) return undefined
|
|
|
const date = new Date(text)
|
|
|
if (Number.isNaN(date.getTime())) return text
|
|
|
return date.getTime()
|
|
|
}
|
|
|
|
|
|
function parseImages(value) {
|
|
|
if (!value) return []
|
|
|
if (Array.isArray(value)) return value.map((v) => String(v)).filter(Boolean)
|
|
|
return String(value).split(',').map((v) => v.trim()).filter(Boolean)
|
|
|
}
|
|
|
|
|
|
function previewImages(list, current) {
|
|
|
if (!list || !list.length) return
|
|
|
uni.previewImage({ urls: list, current })
|
|
|
}
|
|
|
|
|
|
function removeImage(index) {
|
|
|
formData.value.images.splice(index, 1)
|
|
|
}
|
|
|
|
|
|
async function chooseAndUploadImages() {
|
|
|
const remain = Math.max(0, 6 - formData.value.images.length)
|
|
|
if (remain <= 0) return
|
|
|
uni.chooseImage({
|
|
|
count: remain,
|
|
|
sizeType: ['compressed'],
|
|
|
sourceType: ['album', 'camera'],
|
|
|
success: async (res) => {
|
|
|
const paths = Array.isArray(res?.tempFilePaths) ? res.tempFilePaths : []
|
|
|
if (!paths.length) return
|
|
|
uni.showLoading({ title: '上传中', mask: true })
|
|
|
try {
|
|
|
for (const filePath of paths) {
|
|
|
const uploadRes = await uploadMoldImage(filePath)
|
|
|
const url = resolveUploadUrl(uploadRes)
|
|
|
if (url) {
|
|
|
formData.value.images.push(url)
|
|
|
}
|
|
|
}
|
|
|
} catch (e) {
|
|
|
uni.showToast({ title: '图片上传失败', icon: 'none' })
|
|
|
} finally {
|
|
|
uni.hideLoading()
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
|
|
|
function resolveUploadUrl(res) {
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
const data = root?.data !== undefined ? root.data : root
|
|
|
if (typeof data === 'string') return data
|
|
|
if (data && typeof data.url === 'string') return data.url
|
|
|
if (data && typeof data.fullUrl === 'string') return data.fullUrl
|
|
|
if (data && typeof data.path === 'string') return data.path
|
|
|
return ''
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.page-container {
|
|
|
min-height: 100vh;
|
|
|
background-color: #f0f2f5;
|
|
|
}
|
|
|
|
|
|
.filter-card {
|
|
|
background: #ffffff;
|
|
|
margin: 20rpx 24rpx;
|
|
|
border-radius: 18rpx;
|
|
|
padding: 20rpx;
|
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
|
}
|
|
|
|
|
|
.filter-row {
|
|
|
display: flex;
|
|
|
gap: 16rpx;
|
|
|
}
|
|
|
|
|
|
.filter-item {
|
|
|
flex: 1;
|
|
|
background: #f7f9fc;
|
|
|
border-radius: 12rpx;
|
|
|
min-height: 84rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
padding: 0 16rpx;
|
|
|
}
|
|
|
|
|
|
.filter-label {
|
|
|
font-size: 24rpx;
|
|
|
color: #8a9099;
|
|
|
margin-right: 12rpx;
|
|
|
}
|
|
|
|
|
|
.filter-value {
|
|
|
flex: 1;
|
|
|
font-size: 26rpx;
|
|
|
color: #2f353d;
|
|
|
white-space: nowrap;
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
}
|
|
|
|
|
|
.placeholder {
|
|
|
color: #a3a9b2;
|
|
|
}
|
|
|
|
|
|
.filter-arrow {
|
|
|
font-size: 34rpx;
|
|
|
color: #b1b7bf;
|
|
|
}
|
|
|
|
|
|
.search-row {
|
|
|
display: flex;
|
|
|
gap: 14rpx;
|
|
|
margin-top: 16rpx;
|
|
|
}
|
|
|
|
|
|
.search-wrapper {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
background: #f5f7fa;
|
|
|
border-radius: 42rpx;
|
|
|
padding: 0 20rpx;
|
|
|
}
|
|
|
|
|
|
.search-icon {
|
|
|
margin-right: 14rpx;
|
|
|
|
|
|
.iconfont {
|
|
|
font-size: 34rpx;
|
|
|
color: #666666;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.search-input {
|
|
|
flex: 1;
|
|
|
height: 72rpx;
|
|
|
font-size: 28rpx;
|
|
|
color: #333333;
|
|
|
background: transparent;
|
|
|
}
|
|
|
|
|
|
.input-placeholder {
|
|
|
color: #a0a6ad;
|
|
|
}
|
|
|
|
|
|
.clear-btn {
|
|
|
width: 42rpx;
|
|
|
height: 42rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.clear-icon {
|
|
|
font-size: 34rpx;
|
|
|
color: #999999;
|
|
|
}
|
|
|
|
|
|
.search-btn {
|
|
|
width: 140rpx;
|
|
|
height: 72rpx;
|
|
|
border-radius: 36rpx;
|
|
|
background: linear-gradient(135deg, #1a3a5c 0%, #2d5a87 100%);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.search-btn-text {
|
|
|
color: #ffffff;
|
|
|
font-size: 28rpx;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.content-scroll {
|
|
|
height: calc(100vh - 380rpx);
|
|
|
}
|
|
|
|
|
|
.list-wrap {
|
|
|
padding: 0 24rpx 40rpx;
|
|
|
}
|
|
|
|
|
|
.ledger-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;
|
|
|
align-items: center;
|
|
|
border-bottom: 1rpx solid #eef1f4;
|
|
|
padding-bottom: 16rpx;
|
|
|
}
|
|
|
|
|
|
.name {
|
|
|
display: block;
|
|
|
font-size: 32rpx;
|
|
|
color: #1a3a5c;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.code {
|
|
|
display: block;
|
|
|
margin-top: 8rpx;
|
|
|
font-size: 24rpx;
|
|
|
color: #8c929a;
|
|
|
}
|
|
|
|
|
|
.code-wrapper {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 8rpx;
|
|
|
}
|
|
|
|
|
|
.header-right {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 12rpx;
|
|
|
}
|
|
|
|
|
|
.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;
|
|
|
}
|
|
|
|
|
|
.action-icon {
|
|
|
color: #ffffff;
|
|
|
font-size: 28rpx;
|
|
|
}
|
|
|
|
|
|
.edit-btn {
|
|
|
background: #1a3a5c;
|
|
|
}
|
|
|
|
|
|
.delete-btn {
|
|
|
background: #ff4d4f;
|
|
|
}
|
|
|
|
|
|
.status-chip {
|
|
|
padding: 8rpx 16rpx;
|
|
|
border-radius: 999rpx;
|
|
|
font-size: 22rpx;
|
|
|
}
|
|
|
|
|
|
.status-normal {
|
|
|
background: rgba(31, 178, 94, 0.12);
|
|
|
color: #0d9b4f;
|
|
|
}
|
|
|
|
|
|
.status-warning {
|
|
|
background: rgba(255, 153, 0, 0.14);
|
|
|
color: #db8400;
|
|
|
}
|
|
|
|
|
|
.status-danger {
|
|
|
background: rgba(255, 77, 79, 0.14);
|
|
|
color: #de3d40;
|
|
|
}
|
|
|
|
|
|
.card-body {
|
|
|
padding-top: 10rpx;
|
|
|
}
|
|
|
|
|
|
.row {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
margin-top: 12rpx;
|
|
|
}
|
|
|
|
|
|
.label {
|
|
|
font-size: 26rpx;
|
|
|
color: #8a9099;
|
|
|
}
|
|
|
|
|
|
.value {
|
|
|
max-width: 62%;
|
|
|
text-align: right;
|
|
|
font-size: 27rpx;
|
|
|
color: #2f353d;
|
|
|
}
|
|
|
|
|
|
.loading-text,
|
|
|
.empty-text,
|
|
|
.finished-text {
|
|
|
text-align: center;
|
|
|
color: #9aa2ab;
|
|
|
font-size: 26rpx;
|
|
|
padding: 28rpx 0;
|
|
|
}
|
|
|
|
|
|
.go-top-btn {
|
|
|
position: fixed;
|
|
|
right: 28rpx;
|
|
|
bottom: 150rpx;
|
|
|
width: 88rpx;
|
|
|
height: 88rpx;
|
|
|
border-radius: 44rpx;
|
|
|
background: rgba(26, 58, 92, 0.9);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
z-index: 99;
|
|
|
box-shadow: 0 6rpx 16rpx rgba(26, 58, 92, 0.25);
|
|
|
}
|
|
|
|
|
|
.go-top-icon {
|
|
|
color: #ffffff;
|
|
|
font-size: 36rpx;
|
|
|
font-weight: 700;
|
|
|
}
|
|
|
|
|
|
.add-btn {
|
|
|
position: fixed;
|
|
|
right: 28rpx;
|
|
|
bottom: 258rpx;
|
|
|
width: 96rpx;
|
|
|
height: 96rpx;
|
|
|
border-radius: 48rpx;
|
|
|
background: linear-gradient(135deg, #1a3a5c 0%, #2d5a87 100%);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
z-index: 99;
|
|
|
box-shadow: 0 8rpx 24rpx rgba(26, 58, 92, 0.32);
|
|
|
}
|
|
|
|
|
|
.add-icon {
|
|
|
color: #ffffff;
|
|
|
font-size: 56rpx;
|
|
|
line-height: 1;
|
|
|
}
|
|
|
|
|
|
.picker-content {
|
|
|
border-top-left-radius: 20rpx;
|
|
|
border-top-right-radius: 20rpx;
|
|
|
max-height: 70vh;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.picker-header {
|
|
|
padding: 24rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
border-bottom: 1rpx solid #edf0f3;
|
|
|
}
|
|
|
|
|
|
.popup-header {
|
|
|
margin: 10rpx 0 0 20rpx;
|
|
|
}
|
|
|
|
|
|
.picker-title {
|
|
|
font-size: 30rpx;
|
|
|
font-weight: 600;
|
|
|
color: #1a3a5c;
|
|
|
}
|
|
|
|
|
|
.picker-clear-text {
|
|
|
font-size: 26rpx;
|
|
|
color: #2d5a87;
|
|
|
}
|
|
|
|
|
|
.picker-list {
|
|
|
max-height: 58vh;
|
|
|
}
|
|
|
|
|
|
.picker-item {
|
|
|
min-height: 84rpx;
|
|
|
padding: 0 24rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
border-bottom: 1rpx solid #f2f4f7;
|
|
|
}
|
|
|
|
|
|
.picker-text {
|
|
|
flex: 1;
|
|
|
font-size: 28rpx;
|
|
|
color: #2f353d;
|
|
|
white-space: nowrap;
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
}
|
|
|
|
|
|
.picker-check {
|
|
|
font-size: 30rpx;
|
|
|
color: #2d5a87;
|
|
|
margin-left: 20rpx;
|
|
|
}
|
|
|
|
|
|
.popup-content-center {
|
|
|
width: 680rpx;
|
|
|
max-height: 82vh;
|
|
|
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.form-scroll {
|
|
|
max-height: 56vh;
|
|
|
}
|
|
|
|
|
|
.form-content {
|
|
|
padding: 24rpx;
|
|
|
}
|
|
|
|
|
|
.form-item {
|
|
|
margin-bottom: 16rpx;
|
|
|
}
|
|
|
|
|
|
.form-label {
|
|
|
display: block;
|
|
|
margin-bottom: 8rpx;
|
|
|
font-size: 26rpx;
|
|
|
color: #8a9099;
|
|
|
}
|
|
|
|
|
|
.required-star {
|
|
|
color: #ff4d4f;
|
|
|
}
|
|
|
|
|
|
.form-label-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
margin-bottom: 8rpx;
|
|
|
}
|
|
|
|
|
|
.switch-wrap {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10rpx;
|
|
|
}
|
|
|
|
|
|
.switch-label {
|
|
|
font-size: 24rpx;
|
|
|
color: #8a9099;
|
|
|
}
|
|
|
|
|
|
.form-input {
|
|
|
height: 76rpx;
|
|
|
padding: 0 20rpx;
|
|
|
border-radius: 12rpx;
|
|
|
background: #f5f7fa;
|
|
|
font-size: 28rpx;
|
|
|
color: #30363d;
|
|
|
}
|
|
|
|
|
|
.form-input[disabled] {
|
|
|
color: #a3a9b2;
|
|
|
}
|
|
|
|
|
|
.form-picker {
|
|
|
height: 76rpx;
|
|
|
border-radius: 12rpx;
|
|
|
background: #f5f7fa;
|
|
|
padding: 0 20rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
}
|
|
|
|
|
|
.form-picker-text {
|
|
|
flex: 1;
|
|
|
font-size: 28rpx;
|
|
|
color: #30363d;
|
|
|
}
|
|
|
|
|
|
.form-textarea {
|
|
|
width: 100%;
|
|
|
min-height: 130rpx;
|
|
|
border-radius: 12rpx;
|
|
|
background: #f5f7fa;
|
|
|
padding: 16rpx 20rpx;
|
|
|
font-size: 28rpx;
|
|
|
color: #30363d;
|
|
|
}
|
|
|
|
|
|
.image-list {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 14rpx;
|
|
|
}
|
|
|
|
|
|
.image-item,
|
|
|
.image-add {
|
|
|
width: 130rpx;
|
|
|
height: 130rpx;
|
|
|
border-radius: 12rpx;
|
|
|
overflow: hidden;
|
|
|
position: relative;
|
|
|
}
|
|
|
|
|
|
.image-preview {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
background: #edf0f4;
|
|
|
}
|
|
|
|
|
|
.image-add {
|
|
|
background: #f5f7fa;
|
|
|
border: 1rpx dashed #c8ced6;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.image-add-text {
|
|
|
font-size: 52rpx;
|
|
|
color: #8e95a0;
|
|
|
line-height: 1;
|
|
|
}
|
|
|
|
|
|
.image-delete {
|
|
|
position: absolute;
|
|
|
top: 0;
|
|
|
right: 0;
|
|
|
width: 36rpx;
|
|
|
height: 36rpx;
|
|
|
border-bottom-left-radius: 10rpx;
|
|
|
background: rgba(0, 0, 0, 0.45);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.image-delete-text {
|
|
|
color: #ffffff;
|
|
|
font-size: 24rpx;
|
|
|
line-height: 1;
|
|
|
}
|
|
|
|
|
|
.enable-group {
|
|
|
display: flex;
|
|
|
gap: 14rpx;
|
|
|
}
|
|
|
|
|
|
.enable-item {
|
|
|
flex: 1;
|
|
|
height: 72rpx;
|
|
|
border-radius: 12rpx;
|
|
|
background: #f5f7fa;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
font-size: 28rpx;
|
|
|
color: #586070;
|
|
|
}
|
|
|
|
|
|
.enable-item.active {
|
|
|
background: rgba(45, 90, 135, 0.12);
|
|
|
color: #1a3a5c;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.form-footer {
|
|
|
display: flex;
|
|
|
gap: 14rpx;
|
|
|
padding: 20rpx 24rpx 24rpx;
|
|
|
}
|
|
|
|
|
|
.footer-btn {
|
|
|
flex: 1;
|
|
|
height: 76rpx;
|
|
|
border-radius: 12rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.cancel-btn {
|
|
|
background: #edf0f4;
|
|
|
}
|
|
|
|
|
|
.confirm-btn {
|
|
|
background: linear-gradient(135deg, #1a3a5c 0%, #2d5a87 100%);
|
|
|
}
|
|
|
|
|
|
.btn-text {
|
|
|
font-size: 28rpx;
|
|
|
font-weight: 600;
|
|
|
color: #ffffff;
|
|
|
}
|
|
|
|
|
|
.cancel-text {
|
|
|
color: #586070;
|
|
|
}
|
|
|
</style>
|