style:设备运维-样式/中英文调整

master
黄伟杰 2 weeks ago
parent 456678b5d2
commit 85a54b8a3d

@ -37,7 +37,7 @@
<!-- <view class="module-icon" :style="{ background: getModuleColor(moduleIndex) }">
<text class="icon-text">{{ getMenuSymbol(module.name, moduleIndex) }}</text>
</view> -->
<text class="module-title">{{ module.name }}</text>
<text class="module-title">{{ translateLiteral(module.name) }}</text>
</view>
<view class="function-grid">
@ -62,7 +62,7 @@
></u-icon>
<text v-else class="icon-inner">{{ getMenuSymbol(entry.name, entryIndex) }}</text>
</view>
<text class="function-name">{{ entry.name }}</text>
<text class="function-name">{{ translateLiteral(entry.name) }}</text>
</view>
</view>
</view>
@ -78,6 +78,7 @@
<script setup>
import { computed, ref } from 'vue'
import AppEmptyState from '@/components/common/AppEmptyState.vue'
import { translateLiteral } from '@/locales'
import useUserStore from '@/store/modules/user'
import { buildPageModules, findTabMenuByPage, getMenuSymbol, getModuleColor, resolveMenuUrl } from '@/utils/permissionMenu'

@ -27,12 +27,13 @@ import useUserStore from '@/store/modules/user'
import { storeToRefs } from 'pinia'
import { getTabBarMenus } from '@/utils/permissionMenu'
import { useI18n } from 'vue-i18n'
import { translateLiteral } from '@/locales'
const TABBAR_VISIBILITY_EVENT = 'tabbar-visibility-change'
const userStore = useUserStore()
const { menus } = storeToRefs(userStore)
const { t } = useI18n()
const { locale } = useI18n()
const inactiveColor = '#666666'
const activeColor = '#409eff'
@ -86,7 +87,7 @@ function resolveTabIconMeta(menu) {
function createTabItem(menu) {
const iconMeta = resolveTabIconMeta(menu)
return {
text: String(menu.name || menu.enName || '').trim(),
text: translateLiteral(String(menu.name || menu.enName || '').trim()),
icon: iconMeta.icon,
selectedIcon: iconMeta.selectedIcon,
iconType: iconMeta.iconType,
@ -106,6 +107,7 @@ function getCurrentActiveIndex() {
}
const tabList = computed(() => {
locale.value
return getTabBarMenus(menus.value).map(createTabItem)
})

@ -20,7 +20,7 @@
color="#1a3a5c"></u-icon>
<text v-else class="nav-icon-text">{{ item.symbol }}</text>
</view>
<text class="nav-text">{{ item.displayName }}</text>
<text class="nav-text">{{ translateLiteral(item.displayName) }}</text>
<view class="check-badge" @click.stop="handleConfiguredClick(item)">
<u-icon name="minus-circle-fill" size="18" color="#de3730"></u-icon>
</view>
@ -42,7 +42,7 @@
color="#1a3a5c"></u-icon>
<text v-else class="nav-icon-text">{{ item.symbol }}</text>
</view>
<text class="nav-text nav-text-disabled">{{ item.displayName }}</text>
<text class="nav-text nav-text-disabled">{{ translateLiteral(item.displayName) }}</text>
</view>
</view>
</view>
@ -68,6 +68,7 @@ import { useI18n } from 'vue-i18n'
import useUserStore from '@/store/modules/user'
import { getUserNavMenuList, updateUserNavMenuList } from '@/api/system/userNavMenu'
import { getNavPermissionInfo } from '@/api/login'
import { translateLiteral } from '@/locales'
import { buildNavMenuViewModels } from '@/utils/permissionMenu'
const { t } = useI18n()

@ -26,7 +26,7 @@
<u-icon v-else-if="isUviewIcon(item.icon)" :name="getUviewIconName(item.icon)" size="28" color="#1a3a5c"></u-icon>
<text v-else class="nav-icon-text">{{ item.symbol }}</text>
</view>
<text class="nav-text">{{ item.displayName }}</text>
<text class="nav-text">{{ translateLiteral(item.displayName) }}</text>
</view>
</view>
</scroll-view>
@ -37,6 +37,7 @@
<script setup>
import { computed, ref, watch, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
import { translateLiteral } from '@/locales'
const { t } = useI18n()

@ -13,7 +13,7 @@
<u-icon v-else-if="isUviewIcon(item.icon)" :name="getUviewIconName(item.icon)" size="28" :color="item.accentColor"></u-icon>
<text v-else class="nav-icon-text">{{ item.symbol }}</text>
</view>
<text class="nav-text">{{ item.displayName }}</text>
<text class="nav-text">{{ translateLiteral(item.displayName) }}</text>
</view>
</view>
@ -36,6 +36,7 @@ import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { getUserNavMenuList } from '@/api/system/userNavMenu'
import { getNavPermissionInfo } from '@/api/login'
import { translateLiteral } from '@/locales'
import { buildNavMenuViewModels } from '@/utils/permissionMenu'
import NavMenuMore from './NavMenuMore.vue'
import NavMenuEditor from './NavMenuEditor.vue'

@ -181,6 +181,7 @@ export default {
},
functionCommon: {
search: 'Search',
reset: 'Reset',
cancel: 'Cancel',
save: 'Save',
loading: 'Loading...',

@ -113,6 +113,10 @@ const literalMap = {
'点检模板详情': 'moldInspectionPlan.detailTitle',
'点检任务': 'moldTaskConfig.moduleName',
'点检任务详情': 'moldTaskConfig.detailTitle',
'设备台账': 'equipmentLedger.moduleName',
'设备台账详情': 'equipmentLedger.detailTitle',
'设备维修': 'equipmentMaintenance.moduleName',
'设备维修详情': 'equipmentMaintenance.detailTitle',
'设备点检任务': 'equipmentInspectionTasks.moduleName',
'设备点检任务详情': 'equipmentInspectionTasks.detailTitle',
'设备点检记录': 'equipmentInspectionRecord.moduleName',

@ -181,6 +181,7 @@ export default {
},
functionCommon: {
search: '查询',
reset: '重置',
cancel: '取消',
save: '保存',
loading: '加载中...',

@ -2,29 +2,32 @@
<view class="page-container">
<NavBar :title="t('equipmentInspectionRecord.moduleName')" />
<view class="search-card">
<view class="search-row">
<view class="search-input-wrap">
<text class="iconfont icon-search search-icon"></text>
<input
v-model="searchKeyword"
class="search-input"
:placeholder="t('equipmentInspectionRecord.searchPlaceholder')"
confirm-type="search"
@confirm="handleSearch"
/>
</view>
<view class="search-btn" @click="handleSearch">{{ t('functionCommon.search') }}</view>
</view>
<view class="filter-row">
<picker mode="selector" :range="taskTypeLabels" :value="taskTypeIndex" @change="onTaskTypeChange">
<view class="picker-chip">{{ currentTaskTypeLabel }}</view>
</picker>
<picker mode="selector" :range="jobStatusLabels" :value="jobStatusIndex" @change="onJobStatusChange">
<view class="picker-chip">{{ currentJobStatusLabel }}</view>
</picker>
<view class="reset-btn" @click="resetFilters">{{ t('functionCommon.reset') }}</view>
<view class="filter-bar">
<view class="keyword-box">
<input
v-model="searchKeyword"
class="keyword-input"
:placeholder="t('equipmentInspectionRecord.searchPlaceholder')"
:focus="keywordFocus"
confirm-type="search"
@blur="keywordFocus = false"
@input="handleKeywordInput"
@confirm="handleSearch"
/>
</view>
<picker mode="selector" :range="taskTypeLabels" :value="taskTypeIndex" @change="onTaskTypeChange">
<view class="status-box">
<text class="status-box-text">{{ currentTaskTypeLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
</view>
</picker>
<picker mode="selector" :range="jobStatusLabels" :value="jobStatusIndex" @change="onJobStatusChange">
<view class="status-box">
<text class="status-box-text">{{ currentJobStatusLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
</view>
</picker>
<view class="reset-filter-btn" @click="resetFilters">{{ t('functionCommon.reset') }}</view>
</view>
<scroll-view scroll-y class="list-scroll" :scroll-top="scrollTop" @scroll="onScroll" @scrolltolower="loadMore" :lower-threshold="80">
@ -75,7 +78,7 @@
<script setup>
import { computed, nextTick, ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { onLoad, onReachBottom, onShow, onUnload } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { getEquipmentInspectionRecordPage } from '@/api/mes/equipmentInspectionRecord'
@ -94,6 +97,9 @@ const pageNo = ref(1)
const pageSize = ref(10)
const scrollTop = ref(0)
const showGoTop = ref(false)
const keywordFocus = ref(false)
let searchTimer = null
const taskTypeOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
@ -130,10 +136,19 @@ const currentJobStatusLabel = computed(() => {
})
onLoad(async () => {
activateKeywordFocus()
await initAllDict()
await fetchList(true)
})
onShow(() => {
activateKeywordFocus()
})
onUnload(() => {
clearSearchTimer()
})
onReachBottom(() => {
loadMore()
})
@ -182,14 +197,24 @@ function normalizePageData(res) {
}
function handleSearch() {
clearSearchTimer()
uni.hideKeyboard()
fetchList(true)
}
function handleKeywordInput() {
clearSearchTimer()
searchTimer = setTimeout(() => {
fetchList(true)
}, 300)
}
async function resetFilters() {
clearSearchTimer()
searchKeyword.value = ''
selectedTaskType.value = ''
selectedJobStatus.value = ''
activateKeywordFocus()
await nextTick()
await fetchList(true)
}
@ -231,6 +256,20 @@ function goTop() {
scrollTop.value = 0
}
function activateKeywordFocus() {
keywordFocus.value = false
nextTick(() => {
keywordFocus.value = true
})
}
function clearSearchTimer() {
if (searchTimer) {
clearTimeout(searchTimer)
searchTimer = null
}
}
function taskTypeText(value) {
const normalized = String(value)
if (normalized === '1') return t('equipmentInspectionRecord.taskTypeInspect')
@ -289,17 +328,49 @@ function formatDateTime(value) {
<style lang="scss" scoped>
.page-container { min-height: 100vh; background: #f4f5f7; }
.search-card { margin: 20rpx 24rpx 0; padding: 24rpx; background: #fff; border-radius: 20rpx; }
.search-row { display: flex; gap: 16rpx; align-items: center; }
.search-input-wrap { flex: 1; display: flex; align-items: center; gap: 12rpx; padding: 0 20rpx; min-height: 84rpx; background: #f8fafc; border-radius: 16rpx; }
.search-icon { color: #94a3b8; font-size: 30rpx; }
.search-input { flex: 1; font-size: 28rpx; color: #111827; }
.search-btn, .reset-btn { min-width: 120rpx; height: 84rpx; border-radius: 16rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 600; }
.search-btn { background: #1f4b79; color: #fff; }
.filter-row { display: flex; gap: 16rpx; margin-top: 16rpx; flex-wrap: wrap; }
.picker-chip { min-width: 220rpx; padding: 0 20rpx; height: 72rpx; background: #eef4fb; border-radius: 14rpx; color: #1f4b79; font-size: 26rpx; display: flex; align-items: center; }
.reset-btn { background: #eef2f7; color: #475569; height: 72rpx; }
.list-scroll { height: calc(100vh - 304rpx); }
.filter-bar {
display: grid;
grid-template-columns: minmax(0, 0.9fr) 136rpx 136rpx 88rpx;
align-items: center;
gap: 10rpx;
padding: 18rpx 20rpx 20rpx;
}
.keyword-box,
.status-box,
.reset-filter-btn {
height: 64rpx;
background: #ffffff;
border: 1rpx solid #d9dde5;
box-sizing: border-box;
display: flex;
align-items: center;
}
.keyword-box {
padding: 0 16rpx;
}
.keyword-input {
width: 100%;
font-size: 24rpx;
color: #374151;
}
.status-box {
justify-content: space-between;
padding: 0 14rpx;
}
.status-box-text {
font-size: 24rpx;
color: #374151;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.reset-filter-btn {
justify-content: center;
font-size: 24rpx;
color: #4b5563;
}
.list-scroll { height: calc(100vh - 194rpx); }
.list-wrap { padding: 0 24rpx 32rpx; }
.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; }

@ -2,26 +2,26 @@
<view class="page-container">
<NavBar :title="t('equipmentInspectionTasks.moduleName')" />
<view class="search-card">
<view class="search-row">
<view class="search-input-wrap">
<text class="iconfont icon-search search-icon"></text>
<input
v-model="searchKeyword"
class="search-input"
:placeholder="t('equipmentInspectionTasks.searchPlaceholder')"
confirm-type="search"
@confirm="handleSearch"
/>
</view>
<view class="search-btn" @click="handleSearch">{{ t('functionCommon.search') }}</view>
</view>
<view class="filter-row">
<picker mode="selector" :range="taskTypeLabels" :value="taskTypeIndex" @change="onTaskTypeChange">
<view class="picker-chip">{{ currentTaskTypeLabel }}</view>
</picker>
<view class="reset-btn" @click="resetFilters">{{ t('functionCommon.reset') }}</view>
<view class="filter-bar">
<view class="keyword-box">
<input
v-model="searchKeyword"
class="keyword-input"
:placeholder="t('equipmentInspectionTasks.searchPlaceholder')"
:focus="keywordFocus"
confirm-type="search"
@blur="keywordFocus = false"
@input="handleKeywordInput"
@confirm="handleSearch"
/>
</view>
<picker mode="selector" :range="taskTypeLabels" :value="taskTypeIndex" @change="onTaskTypeChange">
<view class="status-box">
<text class="status-box-text">{{ currentTaskTypeLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
</view>
</picker>
<view class="reset-filter-btn" @click="resetFilters">{{ t('functionCommon.reset') }}</view>
</view>
<scroll-view scroll-y class="list-scroll" :scroll-top="scrollTop" @scroll="onScroll" @scrolltolower="loadMore" :lower-threshold="80">
@ -65,7 +65,7 @@
<script setup>
import { computed, nextTick, ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { onLoad, onReachBottom, onShow, onUnload } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { getTaskManagementPage, createTaskManagementTicket } from '@/api/mes/taskManagement'
@ -85,6 +85,9 @@ const scrollTop = ref(0)
const showGoTop = ref(false)
const ticketLoadingId = ref(null)
const deviceOptions = ref([])
const keywordFocus = ref(false)
let searchTimer = null
const taskTypeOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
@ -103,9 +106,18 @@ const currentTaskTypeLabel = computed(() => {
})
onLoad(async () => {
activateKeywordFocus()
await Promise.all([ensureDeviceOptionsLoaded(), fetchList(true)])
})
onShow(() => {
activateKeywordFocus()
})
onUnload(() => {
clearSearchTimer()
})
onReachBottom(() => {
loadMore()
})
@ -171,13 +183,23 @@ function normalizePageData(res) {
}
function handleSearch() {
clearSearchTimer()
uni.hideKeyboard()
fetchList(true)
}
function handleKeywordInput() {
clearSearchTimer()
searchTimer = setTimeout(() => {
fetchList(true)
}, 300)
}
async function resetFilters() {
clearSearchTimer()
searchKeyword.value = ''
selectedTaskType.value = ''
activateKeywordFocus()
await nextTick()
await fetchList(true)
}
@ -226,6 +248,20 @@ function goTop() {
scrollTop.value = 0
}
function activateKeywordFocus() {
keywordFocus.value = false
nextTick(() => {
keywordFocus.value = true
})
}
function clearSearchTimer() {
if (searchTimer) {
clearTimeout(searchTimer)
searchTimer = null
}
}
function parseIdsValue(value) {
if (!value) return []
if (Array.isArray(value)) return value.map((item) => String(item).trim()).filter(Boolean)
@ -279,17 +315,45 @@ function formatDate(value) {
<style lang="scss" scoped>
.page-container { min-height: 100vh; background: #f4f5f7; }
.search-card { margin: 20rpx 24rpx 0; padding: 24rpx; background: #fff; border-radius: 20rpx; }
.search-row { display: flex; gap: 16rpx; align-items: center; }
.search-input-wrap { flex: 1; display: flex; align-items: center; gap: 12rpx; padding: 0 20rpx; min-height: 84rpx; background: #f8fafc; border-radius: 16rpx; }
.search-icon { color: #94a3b8; font-size: 30rpx; }
.search-input { flex: 1; font-size: 28rpx; color: #111827; }
.search-btn, .reset-btn { min-width: 120rpx; height: 84rpx; border-radius: 16rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 600; }
.search-btn { background: #1f4b79; color: #fff; }
.filter-row { display: flex; gap: 16rpx; margin-top: 16rpx; }
.picker-chip { min-width: 220rpx; padding: 0 20rpx; height: 72rpx; background: #eef4fb; border-radius: 14rpx; color: #1f4b79; font-size: 26rpx; display: flex; align-items: center; }
.reset-btn { background: #eef2f7; color: #475569; height: 72rpx; }
.list-scroll { height: calc(100vh - 272rpx); }
.filter-bar {
display: grid;
grid-template-columns: minmax(0, 1fr) 150rpx 96rpx;
align-items: center;
gap: 14rpx;
padding: 18rpx 28rpx 20rpx;
}
.keyword-box,
.status-box,
.reset-filter-btn {
height: 66rpx;
background: #ffffff;
border: 1rpx solid #d9dde5;
box-sizing: border-box;
display: flex;
align-items: center;
}
.keyword-box {
padding: 0 20rpx;
}
.keyword-input {
width: 100%;
font-size: 26rpx;
color: #374151;
}
.status-box {
justify-content: space-between;
padding: 0 18rpx;
}
.status-box-text {
font-size: 26rpx;
color: #374151;
}
.reset-filter-btn {
justify-content: center;
font-size: 24rpx;
color: #4b5563;
}
.list-scroll { height: calc(100vh - 194rpx); }
.list-wrap { padding: 0 24rpx 32rpx; }
.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; }

@ -40,15 +40,29 @@ const MENU_ROUTE_MAP = {
equipmentcategory: '/pages_function/pages/equipmentCategory/index',
'设备台账': '/pages_function/pages/equipmentLedger/index',
equipmentledger: '/pages_function/pages/equipmentLedger/index',
equipmentledgers: '/pages_function/pages/equipmentLedger/index',
deviceledger: '/pages_function/pages/equipmentLedger/index',
deviceledgers: '/pages_function/pages/equipmentLedger/index',
'设备维修': '/pages_function/pages/equipmentMaintenance/index',
'设备报修': '/pages_function/pages/equipmentMaintenance/index',
'设备维修工单': '/pages_function/pages/equipmentMaintenance/index',
equipmentmaintenance: '/pages_function/pages/equipmentMaintenance/index',
equipmentrepair: '/pages_function/pages/equipmentMaintenance/index',
devicerepair: '/pages_function/pages/equipmentMaintenance/index',
dvrepair: '/pages_function/pages/equipmentMaintenance/index',
'设备点检任务': '/pages_function/pages/equipmentInspectionTasks/index',
'设备点检工单': '/pages_function/pages/equipmentInspectionTasks/index',
equipmentinspectiontasks: '/pages_function/pages/equipmentInspectionTasks/index',
equipmentinspectiontask: '/pages_function/pages/equipmentInspectionTasks/index',
deviceinspectiontasks: '/pages_function/pages/equipmentInspectionTasks/index',
deviceinspectiontask: '/pages_function/pages/equipmentInspectionTasks/index',
taskmanagement: '/pages_function/pages/equipmentInspectionTasks/index',
'设备点检记录': '/pages_function/pages/equipmentInspectionRecord/index',
'设备点检记录查询': '/pages_function/pages/equipmentInspectionRecord/index',
equipmentinspectionrecord: '/pages_function/pages/equipmentInspectionRecord/index',
equipmentinspectionrecords: '/pages_function/pages/equipmentInspectionRecord/index',
deviceinspectionrecord: '/pages_function/pages/equipmentInspectionRecord/index',
deviceinspectionrecords: '/pages_function/pages/equipmentInspectionRecord/index',
'设备关键件': '/pages_function/pages/criticalComponent/index',
criticalcomponent: '/pages_function/pages/criticalComponent/index',
equipmentkeypart: '/pages_function/pages/equipmentKeypart/index',

Loading…
Cancel
Save