Browse Source

fix: 接口调试: 日期时间, 容器, 溶液, 登录, 用户管理

master
guoapeng 1 week ago
parent
commit
9a083e0d6e
  1. 1
      eslint.config.js
  2. 8
      src/apis/solution.ts
  3. 4
      src/apis/system.ts
  4. 2
      src/apis/user.ts
  5. 35
      src/components/common/FTImage/index.vue
  6. 2
      src/components/common/FTTable/index.vue
  7. 68
      src/components/container/Item/index.vue
  8. 40
      src/components/craft/AddCraft/index.vue
  9. 283
      src/components/home/AddLiquid/index.vue
  10. 29
      src/components/system/EditDate/index.vue
  11. 35
      src/components/user/Edit/index.vue
  12. 64
      src/hooks/useServerTime.ts
  13. 19
      src/layouts/default.vue
  14. 12
      src/libs/socket.ts
  15. 3
      src/libs/utils.ts
  16. 2
      src/router/routes.ts
  17. 12
      src/stores/systemStore.ts
  18. 12
      src/types/container.d.ts
  19. 4
      src/types/system.d.ts
  20. 3
      src/types/user.d.ts
  21. 4
      src/views/container/index.vue
  22. 21
      src/views/debug/index.vue
  23. 46
      src/views/home/index.vue
  24. 2
      src/views/user/index.vue

1
eslint.config.js

@ -15,6 +15,7 @@ export default lintConfig({
'ts/no-use-before-define': 0,
'no-alert': 0,
'unused-imports/no-unused-vars': 0,
'unused-imports/no-unused-imports': 0,
},
globals: { process: 'readonly' },
extends: [

8
src/apis/solution.ts

@ -1,9 +1,9 @@
import http from 'libs/http'
export const getSolsList = (params: System.Page = { pageNum: 1, pageSize: 999 }): Promise<System.PageResponse<Solution.SolutionItem>> => http.get(`/sols/list`, { params })
export const getSolsList = (params: System.Page = { pageNum: 1, pageSize: 999 }): Promise<System.PageResponse<Solution.SolutionItem>> => http.post(`/solutions/list`, params)
export const saveSols = (params: { name: string }): Promise<null> => http.post(`/sols`, params)
export const saveSols = (params: { name: string }): Promise<null> => http.post(`/solutions`, params)
export const editSols = (params: { id: number, name: string }): Promise<null> => http.put(`/sols`, params)
export const editSols = (params: { id: number, name: string }): Promise<null> => http.put(`/solutions`, params)
export const delSols = (ids: string): Promise<null> => http.delete(`/sols/${ids}`)
export const delSols = (ids: string): Promise<null> => http.delete(`/solutions/${ids}`)

4
src/apis/system.ts

@ -6,7 +6,7 @@ export const getStatus = (): Promise<System.SystemStatus> => http.get('/sys/devi
export const getPoint = (motor: string): Promise<string> => http.get(`/motor/position/${motor}`)
export const requireOutTray = (): Promise<{ moduleCode: string }[]> => http.get('/self-test/require-out-tray')
export const setIgnoreItem = (params: { ignoreSelfTestType: string, ignore: boolean }): Promise<null> => http.post('/self-test/set-ignore-item', params)
export const getTime = (): Promise<number> => http.get('/sys/datetime')
export const setTime = (params: { datetime?: string }): Promise<number> => http.post('/sys/datetime', params)
export const getTime = (): Promise<{ epochMilli: number }> => http.get('/sys/get-datetime')
export const setTime = (params: { epochMilli?: number }): Promise<null> => http.post('/sys/set-datetime', params)
export const configList = (): Promise<any[]> => http.get('/sys/config-list')
export const updateConfig = (params: System.SystemConfig): Promise<number> => http.post('/sys/update-config', params)

2
src/apis/user.ts

@ -3,5 +3,5 @@ import http from 'libs/http'
export const current = (): Promise<User.User> => http.get('/auth/current')
export const addUser = (params: User.User): Promise<null> => http.post('/user', params)
export const updateUser = (params: User.User): Promise<null> => http.put('/user', params)
export const userList = (params: System.Page): Promise<System.PageResponse<User.User>> => http.get('/user/list', { params })
export const userList = (params: System.Page): Promise<System.PageResponse<User.User>> => http.post('/user/list', params)
export const delUser = (params: string): Promise<null> => http.delete(`/user/${params}`)

35
src/components/common/FTImage/index.vue

@ -0,0 +1,35 @@
<script setup lang="ts">
import { Knova } from 'knova'
// knova
const knovaInstance = ref<Knova | null>(null)
const images = ref([
'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg',
'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg',
])
onMounted(() => {
const container = document.getElementById('knova-container')
if (container) {
// knova
knovaInstance.value = new Knova(container, {
items: images.value.map(src => ({
src,
type: 'image',
})),
gestures: true, //
zoom: true, //
doubleTap: true, //
})
}
})
</script>
<template lang="pug">
div#knova-container
</template>
<style scoped lang="scss">
</style>

2
src/components/common/FTTable/index.vue

@ -173,7 +173,7 @@ defineExpose({
</el-table>
</div>
<div v-if="hasPage" class="table-page">
<el-pagination v-model:current-page="state.filterObj.pageNum" v-model:page-size="state.filterObj.pageSize" size="small" background layout="sizes, prev, pager, next" :total="state.dataTotal" @change="initData" />
<el-pagination v-model:current-page="state.filterObj.pageNum" v-model:page-size="state.filterObj.pageSize" size="small" background layout="sizes, prev, pager, next, total" :total="state.dataTotal" @change="initData" />
</div>
</div>
</template>

68
src/components/container/Item/index.vue

@ -20,9 +20,6 @@ const props = defineProps({
const emits = defineEmits<{
(e: 'ok'): void
}>()
const router = useRouter()
const visible = ref(false)
const solutionStore = useSolutionStore()
const solutionId = ref()
const selectedSolutionItem = ref()
@ -68,38 +65,20 @@ const saveContainer = async () => {
}
const params: Container.ContainerItem = {
id: solutionInfo.value.id,
type: 0,
solutionId: solutionInfo.value.solutionId,
pumpId: solutionInfo.value.pumpId,
capacityTotal: solutionInfo.value.capacityTotal,
capacityUsed: solutionInfo.value.capacityUsed,
filled: solutionInfo.value.filled,
}
await updateContainer(params)
FtMessage.success('保存成功')
emits('ok')
}
const onSolutionChange = (value: number) => {
if (value) {
solutionId.value = value
solutionInfo.value.solutionId = value
selectedSolutionItem.value = solutionStore.solutionList.filter(item => item.id === value)[0]
}
}
const onClose = () => {
solutionId.value = null
visible.value = false
}
const onSubmitSolution = () => {
if (!solutionStore.solutionList.length) {
//
router.push('/solution')
return
const onSolutionChange = async () => {
const params: Container.ContainerItem = {
id: solutionInfo.value.id,
solutionId: solutionInfo.value.solutionId,
}
solutionInfo.value.solutionName = selectedSolutionItem.value.name
solutionInfo.value.solutionId = selectedSolutionItem.value.id
saveContainer()
onClose()
await updateContainer(params)
FtMessage.success('保存成功')
}
</script>
@ -107,10 +86,9 @@ const onSubmitSolution = () => {
<div class="liquied-item">
<div class="header">
<div class="solution-select">
<div class="solution-name">
<span v-if="solutionInfo.solutionName">{{ solutionInfo.solutionName }}</span>
<span v-else style="color:#d2d2d2">选择酸液</span>
</div>
<el-select v-model="solutionInfo.solutionId" placeholder="请选择酸液" style="width: 80%" @change="onSolutionChange">
<el-option v-for="item in solutionStore.solutionList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</div>
</div>
<div class="content">
@ -134,20 +112,6 @@ const onSubmitSolution = () => {
</el-input>
</div>
</div>
<FtDialog v-model="visible" title="选择酸液" :ok-handle="onSubmitSolution" @cancel="onClose">
<div v-if="solutionStore.solutionList.length">
<el-radio-group v-model="solutionId" size="large" class="radio-group" @change="onSolutionChange">
<el-radio-button v-for="item in solutionStore.solutionList" :key="item.id" :label="item.name" :value="item.id" class="radio-marge" />
</el-radio-group>
</div>
<div v-else>
<el-empty description="description">
<template #description>
未添加酸液请在溶液管理中配置
</template>
</el-empty>
</div>
</FtDialog>
</div>
</template>
@ -206,20 +170,12 @@ const onSubmitSolution = () => {
}
.solution-select{
display:flex;
width: 120px;
width: 100%;
height: 25px;
text-align: center;
margin-left: 5px;
justify-content: center;
align-items: center;
border-radius: 5px;
}
.solution-name{
width: 100px;
height: 25px;
display: flex;
align-items: center;
justify-content: center;
}
.add-icon{
margin-right: -4px;
}

40
src/components/craft/AddCraft/index.vue

@ -149,35 +149,46 @@ const cancel = () => {
}
const stepMap = {
preHeat: { name: '预热', method: 'preHeat', params: { type: 'heat', temperature: undefined, description: undefined } },
preHeat: { name: '移至滴定位', method: 'preHeat', params: { type: 'heat', temperature: undefined, description: undefined } },
addThin: {
name: '加稀硝酸',
name: '选择溶液',
method: 'addThin',
params: { volume: undefined, description: undefined },
},
addThick: {
name: '加浓硝酸',
name: '移至加热位',
method: 'addThick',
params: { height: undefined, volume: undefined, description: undefined },
},
heat: {
name: '加热',
name: '开始滴定',
method: 'heat',
params: { temperature: undefined, time: undefined, description: undefined, minutes: undefined, seconds: undefined },
},
// reduceLiquid: { name: '', method: 'reduceLiquid', params: { height: undefined, description: undefined } },
clean: {
name: '清洗',
name: '加入磁子',
method: 'clean',
params: { cycle: undefined, height: undefined, volume: 2, description: undefined },
},
dry: {
name: '烘干',
name: '开始搅拌',
method: 'dry',
params: { temperature: undefined, time: undefined, description: undefined, minutes: undefined, seconds: undefined },
},
anneal: {
name: '退火',
name: '等待',
method: 'anneal',
params: {
temperature: undefined,
time: undefined,
description: undefined,
minutes: undefined,
seconds: undefined,
},
},
heater: {
name: '开始加热',
method: 'anneal',
params: {
temperature: undefined,
@ -202,6 +213,15 @@ const addStep = (data: any) => {
<el-form-item label="工艺名称" prop="name">
<el-input v-model="form.name" placeholder="请输入工艺名称" />
</el-form-item>
<el-form-item label="液体体积" prop="name">
<el-input v-model="form.name" placeholder="请输入工艺名称" />
</el-form-item>
<el-form-item label="空白体积" prop="name">
<el-input v-model="form.name" placeholder="请输入工艺名称" />
</el-form-item>
<el-form-item label="溶液浓度" prop="name">
<el-input v-model="form.name" placeholder="请输入工艺名称" />
</el-form-item>
<el-form-item label="步骤列表">
<div class="button-content">
<el-tag v-for="item in stepMap" :key="item" size="large" @click="() => addStep(item)">
@ -320,7 +340,7 @@ const addStep = (data: any) => {
<style scoped lang="scss">
.form-box {
height: 50vh;
height: 60vh;
.el-row {
height: 100%;
.el-col:first-child {
@ -375,9 +395,9 @@ const addStep = (data: any) => {
.el-input,
.el-select {
width: 120px;
margin: 0 5px;
}
margin: 0 5px;}
.info-box {
width: 80%;
height: 100%;
}

283
src/components/home/AddLiquid/index.vue

@ -1,202 +1,101 @@
<script setup lang="ts">
import { getContainerList } from 'apis/container'
import { getSolsList } from 'apis/solution'
import { socket } from 'libs/socket'
import { useHomeStore } from 'stores/homeStore'
import { useSystemStore } from 'stores/systemStore'
import { onMounted, onUnmounted, ref } from 'vue'
const emits = defineEmits(['ok', 'cancel'])
const homeStore = useHomeStore()
const systemStore = useSystemStore()
onMounted(async () => {
await getSols()
await queryContainerList()
socket.init(receiveMessage, 'cmd_debug')
socket.init(receiveMessage, 'cmd_response')
defineProps({
visible: {
type: Boolean,
default: false,
},
})
const emits = defineEmits(['close'])
onUnmounted(() => {
socket.unregisterCallback(receiveMessage, 'cmd_debug')
socket.unregisterCallback(receiveMessage, 'cmd_response')
})
const queryContainerList = async () => {
containerList.value = await getContainerList()
}
let currentCommandId = ''
const receiveMessage = (data: Socket.cmdData) => {
data.commandId === currentCommandId && systemStore.pushSystemList(data)
}
const form = ref<{
columns?: number[]
containerId?: number
volume?: number
}>({})
const formRef = ref()
const validateHandle = (rule: any, value: any, callback: any) => {
if (!value?.length) {
callback(new Error('请选择试管'))
}
else {
callback()
}
}
const rules = {
columns: [
{ required: true, message: '请选择试管', trigger: 'change', validator: validateHandle },
],
containerId: [
{ required: true, message: '请选择溶液', trigger: 'change' },
],
volume: [
{ required: true, message: '请输入容量', trigger: 'blur' },
],
}
const activeTab = ref(1)
const okHandle = async () => {
try {
const valid = await formRef.value.validate()
if (!valid) {
return
}
currentCommandId = Date.now().toString()
const params = {
commandId: currentCommandId,
command: 'liquid_add',
params: form.value,
}
await homeStore.sendControl(params)
emits('ok')
}
catch (error) {
console.log(error)
}
}
const cancel = () => {
emits('cancel')
}
const solsList = ref<Solution.SolutionItem[]>([])
const containerList = ref<Container.ContainerItem[]>([])
const getSols = async () => {
const res = await getSolsList()
solsList.value = res.list
}
//
// const tubes = computed(() => {
// const tray = systemStore.systemStatus.trays?.find(item => item.inSolutionPositon)
// return tray?.tubes || []
// })
const selectedColumns = ref(Array.from({ length: 5 }).fill(false))
const mousedownHandle = async (index: number) => {
// if (!tubes.value.find(item => item.columnNum === index)?.exists) {
// FtMessage.error('')
// return
// }
selectedColumns.value[index - 1] = !selectedColumns.value[index - 1]
form.value.columns = selectedColumns.value.map((item, index) => {
return item ? index + 1 : false
}).filter(item => item !== false)
formRef.value.validateField('columns')
const close = () => {
emits('close')
}
</script>
<template>
<FtDialog visible title="添加溶液" width="40%" :ok-handle="okHandle" @cancel="cancel">
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto">
<el-form-item label="选择试管" prop="columns">
<div class="tube-item">
<div
v-for="item in 5"
:key="item"
class="tube-line"
:class="{ 'tube-line-active': selectedColumns[item - 1] }"
@click.prevent="() => mousedownHandle(item)"
@touch.prevent="() => mousedownHandle(item)"
>
<span v-for="i in 8" :key="i" class="tube-line-inner" />
</div>
</div>
</el-form-item>
<el-form-item label="选择溶液" prop="solutionId">
<el-select v-model="form.containerId" placeholder="请选择溶液">
<el-option v-for="item in containerList.filter(i => i.type === 0)" :key="item.id" :label="solsList.find(s => s.id === item.solutionId)?.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="容量" prop="volume">
<el-input v-model="form.volume" type="number" placeholder="请输入容量">
<template #append>
mL
</template>
</el-input>
</el-form-item>
</el-form>
</FtDialog>
<template lang="pug">
div.dialog-box(v-if="visible")
div.title
p(:class="{ 'tab-active': activeTab === 1 }", @click="activeTab = 1")
| 滴定位1
p(:class="{ 'tab-active': activeTab === 2 }", @click="activeTab = 2")
| 滴定位2
el-icon.close-icon(@click="close")
Close
div.body
div.button-box
ft-button
| 移至加热
ft-button
| 移至托盘
ft-button
| 加入磁子
div.form-box
span
| 选择溶液
el-select
el-input
template(#append)
el-select
ft-button
| 添加
div.form-box
span
| 搅拌设置
el-input
template(#append)
| ms
el-input
template(#append)
| s
ft-button
| 开始
</template>
<style scoped lang="scss">
.el-tag {
margin-right: 5px;
}
.el-row {
height: 450px;
.el-col {
height: 100%;
overflow: auto;
:deep(.el-tag) {
width: 100%;
margin-bottom: 5px;
.el-tag__content {
display: flex;
width: 100%;
justify-content: space-between;
}
}
}
}
.tube-item {
padding: 5px;
background: #384D5D;
border-radius: 10px;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(1, 1fr);
grid-gap: 5px;
position: relative;
.tube-line {
display: flex;
flex-direction: column;
.tube-line-inner {
display: inline-block;
width: 25px;
height: 25px;
border-radius: 50%;
background: #fff;
margin: 2px;
transition: background 0.5s;
}
}
.tube-line-disable {
.tube-line-inner {
background: #C6C6C6;
}
}
.tube-line-active {
.tube-line-inner {
background: #26D574;
}
}
}
<style scoped lang="stylus">
.dialog-box
position absolute
width 500px
bottom 0
right 250px
box-shadow var(--el-box-shadow-light)
background #fff
.title
background #F5F7FA
height 30px
display flex
color #818181
position relative
.close-icon
font-size 20px
position absolute
right 10px
top 5px
.tab-active
color #12C290
background #fff
p
height 100%
line-height 30px
text-align center
width 100px
.body
padding 10px 10px 0
.button-box
width 100%
display grid
grid-template-columns repeat(3, 1fr)
gap 20px
.form-box
padding 5px
background #F5F9FD
display grid
grid-template-columns 1fr 2fr 2fr 1.5fr
gap 10px
align-items center
margin 10px 0
:deep(.el-input-group__append)
width 80px
padding 0
</style>

29
src/components/system/EditDate/index.vue

@ -1,18 +1,29 @@
<script setup lang="ts">
import { setTime } from 'apis/system'
import { useServerTime } from 'hooks/useServerTime'
import { FtMessage } from 'libs/message'
import { useSystemStore } from 'stores/systemStore'
import { ref } from 'vue'
const emits = defineEmits(['ok', 'close'])
const form = ref<{ datetime?: string }>({
datetime: useSystemStore().currentTime,
const { serverTime, setDateTime } = useServerTime()
console.log(serverTime.value)
const form = ref<{ epochMilli?: number }>({
epochMilli: serverTime.value,
})
let onceFlag = false
watch(() => serverTime.value, () => {
if (!onceFlag) {
onceFlag = true
form.value.epochMilli = serverTime.value
}
})
const formRef = ref()
const rules = {
datetime: [
epochMilli: [
{ required: true, message: '请选择时间', trigger: 'change' },
],
}
@ -23,7 +34,7 @@ const okHandle = async () => {
if (!valid) {
return
}
await setTime(form.value)
await setDateTime(form.value.epochMilli as number)
FtMessage.success('修改成功')
emits('ok')
}
@ -40,12 +51,12 @@ const cancel = () => {
<template>
<ft-dialog visible title="设置日期与时间" width="40%" :ok-handle="okHandle" @cancel="cancel">
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto">
<el-form-item label="日期与时间" prop="datetime">
<el-form-item label="日期与时间" prop="epochMilli">
<el-date-picker
v-model="form.datetime"
v-model="form.epochMilli"
:show-now="false"
format="YYYY-MM-DD hh:mm:ss"
value-format="YYYY-MM-DD hh:mm:ss"
value-format="x"
style="width: 100%"
type="datetime"
placeholder="请选择日期和时间"

35
src/components/user/Edit/index.vue

@ -14,6 +14,7 @@ const props = defineProps({
const emits = defineEmits(['ok', 'cancel'])
onMounted(() => {
form.value.password = undefined
})
onUnmounted(() => {
@ -60,6 +61,9 @@ const rules = {
username: [
{ required: true, message: '请输入账号', trigger: 'blur', validator: validateHandle },
],
role: [
{ required: true, message: '请选择角色', trigger: 'change' },
],
nickname: [
{ required: false, message: '请输入用户名', trigger: 'blur' },
],
@ -81,11 +85,16 @@ const okHandle = async () => {
await updateUser(form.value)
}
else {
form.value.role = 'USER'
form.value.deleted = 'DISABLE'
form.value.fixedUser = 'DISABLE'
await addUser(form.value)
}
// , 退
if (form.value.id === useSystemStore().systemStatus.currentUser?.id) {
useSystemStore().logout()
}
FtMessage.success('保存成功')
emits('ok')
}
catch (e) {
@ -96,6 +105,21 @@ const okHandle = async () => {
const cancel = () => {
emits('cancel')
}
const roleList = [
{
label: '管理员',
value: 'ADMIN',
},
{
label: '开发者',
value: 'DEVELOPER',
},
{
label: '普通用户',
value: 'USER',
},
]
</script>
<template>
@ -107,11 +131,16 @@ const cancel = () => {
<el-form-item label="用户名" prop="nickname">
<el-input v-model="form.nickname" maxlength="10" show-word-limit :disabled="data.id && form.fixedUser === 'ENABLE'" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" placeholder="请选择角色">
<el-option v-for="item in roleList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item v-if="useSystemStore().systemStatus.currentUser?.fixedUser === 'ENABLE'" label="密码" prop="password">
<el-input v-model="form.password" maxlength="6" show-word-limit show-password type="password" placeholder="请输入密码" />
<el-input v-model="form.password" maxlength="6" autocomplete="new-password" show-word-limit show-password type="password" placeholder="请输入密码" />
</el-form-item>
<el-form-item v-if="useSystemStore().systemStatus.currentUser?.fixedUser === 'ENABLE'" label="再次输入密码" prop="passwordAgain">
<el-input v-model="form.passwordAgain" maxlength="6" show-word-limit show-password type="password" placeholder="请输入密码" />
<el-input v-model="form.passwordAgain" maxlength="6" autocomplete="new-password" show-word-limit show-password type="password" placeholder="请输入密码" />
</el-form-item>
</el-form>
</FtDialog>

64
src/hooks/useServerTime.ts

@ -1,44 +1,64 @@
import { getTime } from 'apis/system'
import { getTime, setTime } from 'apis/system'
import { formatDateTime } from 'libs/utils'
import { onMounted, onUnmounted, ref } from 'vue'
export function useServerTime() {
const serverTime = ref<number | null>(null) // 初始服务器时间戳
const serverTime = ref<number | undefined>() // 初始服务器时间戳
const clientFetchTime = ref<number | null>(null) // 获取服务器时间时的客户端时间戳
const currentTime = ref<string>('0000-00-00 00:00:00')
const currentTime = ref<string>('0001-00-00 00:00:00')
const editVisible = ref(false)
let interval: number | null = null
onMounted(async () => {
try {
serverTime.value = await getTime()
currentTime.value = formatDateTime(undefined, new Date(serverTime.value))
clientFetchTime.value = Date.now()
let num = 0
interval = window.setInterval(async () => {
if (num > 60) {
serverTime.value = await getTime()
clientFetchTime.value = Date.now()
num = 0
}
if (serverTime.value && clientFetchTime.value) {
const elapsed = Date.now() - clientFetchTime.value
const currentServerTime = serverTime.value + elapsed
currentTime.value = formatDateTime(undefined, new Date(currentServerTime))
num++
}
}, 1000)
await getDateTime()
}
// eslint-disable-next-line unused-imports/no-unused-vars
catch (error) {
// currentTime.value = formatDateTime(undefined, new Date())
console.error('获取服务器时间失败')
}
})
const openDialog = () => {
editVisible.value = true
}
const closeDialog = () => {
editVisible.value = false
}
const getDateTime = async () => {
serverTime.value = (await getTime())?.epochMilli
clientFetchTime.value = Date.now()
let num = 0
if (interval)
clearInterval(interval)
interval = window.setInterval(async () => {
if (num > 60) {
serverTime.value = (await getTime())?.epochMilli
clientFetchTime.value = Date.now()
num = 0
}
if (serverTime.value && clientFetchTime.value) {
const elapsed = Date.now() - clientFetchTime.value
const currentServerTime = serverTime.value + elapsed
currentTime.value = formatDateTime(undefined, new Date(currentServerTime))
num++
}
}, 1000)
}
const setDateTime = async (time: number) => {
await setTime({ epochMilli: time })
await getDateTime()
}
onUnmounted(() => {
if (interval)
clearInterval(interval)
})
return { currentTime }
return { currentTime, serverTime, editVisible, setDateTime, openDialog, closeDialog }
}

19
src/layouts/default.vue

@ -20,7 +20,12 @@ import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
const { handleLogoClick } = useActivateDebug()
const { currentTime } = useServerTime()
const { currentTime, openDialog, closeDialog, editVisible } = useServerTime()
watch(() => currentTime.value, () => {
systemStore.currentTime = currentTime.value
})
const systemStore = useSystemStore()
const router = useRouter()
@ -61,10 +66,6 @@ const receiveMessage = async (data: any) => {
flag = false
}
watch(() => currentTime.value, () => {
systemStore.currentTime = currentTime.value
})
watch (() => isClose.value, async (newVal) => {
if (newVal) {
await checkCraft()
@ -149,10 +150,8 @@ const commandHandle = async (command: string, params?: unknown) => {
await useDebugStore().sendControl(data)
}
const editDateVisible = ref(false)
const containerStatus = computed(() => {
const empty = systemStore.systemStatus.solutionModule.solutionContainer.find(item => item.empty)
const empty = systemStore.systemStatus.solutionModule?.solutionContainer?.find(item => item.empty)
if (empty) {
return 'empty'
}
@ -190,7 +189,7 @@ const containerStatus = computed(() => {
</template>
</el-dropdown>
<div class="time" @click="editDateVisible = true">
<div class="time" @click="openDialog">
{{ currentTime }}
</div>
<div class="user">
@ -306,7 +305,7 @@ const containerStatus = computed(() => {
<Check v-if="isCheck" @close="isCheck = false" />
<Stop v-if="systemStore.systemStatus.emergencyStop" />
<CheckCraft v-if="checkCraftVisible" @close="checkCraftVisible = false" />
<EditDate v-if="editDateVisible" @close="editDateVisible = false" @ok="editDateVisible = false" />
<EditDate v-if="editVisible" @close="closeDialog" @ok="closeDialog" />
</el-container>
</template>

12
src/libs/socket.ts

@ -22,6 +22,7 @@ interface socket {
reconnect_timer: any
reconnect_interval: number
receiveMessageCallBackObj: { [key: string]: any[] }
receiveMessageCallBackTimestampObj: { [key: string]: number }
initCallBacks: any
// eslint-disable-next-line ts/no-unsafe-function-type
receiveMessage: Function
@ -66,6 +67,7 @@ export const socket: socket = {
reconnect_interval: 1000,
receiveMessageCallBackObj: {},
receiveMessageCallBackTimestampObj: {},
initCallBacks: [],
// eslint-disable-next-line ts/no-unsafe-function-type
registerInitCallback: (fn: Function, ...args: any) => {
@ -80,9 +82,13 @@ export const socket: socket = {
const message = JSON.parse(e.data)
const callbacks = socket.receiveMessageCallBackObj[message.type]
if (callbacks) {
callbacks.forEach((fn) => {
fn(message.data)
})
const timestamp = socket.receiveMessageCallBackTimestampObj[message.type] || 0
if (message.timestamp > timestamp) {
socket.receiveMessageCallBackTimestampObj[message.type] = message.timestamp
callbacks.forEach((fn) => {
fn(message.data)
})
}
}
else {
// console.error('请注册当前类型的回调函数', message)

3
src/libs/utils.ts

@ -1,7 +1,4 @@
export const cmdNameMap = {
door_open: '开门',
door_close: '关门',
door_stop: '停止开关门',
liquid_motor_move_to: '加液电机绝对移动',
liquid_motor_origin: '加液电机回原点',
liquid_motor_stop: '加液电机停止',

2
src/router/routes.ts

@ -73,7 +73,7 @@ const authRoutes: RouteRecordRaw[] = [
name: 'solution',
component: () => import('views/solution/index.vue'),
meta: {
isDefault: false,
isDefault: true,
title: '溶液管理',
icon: n_liquid_config,
activeIcon: s_liquid_config,

12
src/stores/systemStore.ts

@ -11,10 +11,6 @@ export const useSystemStore = defineStore('system', {
selfTest: true,
emergencyStop: false,
currentUser: {
id: 1,
username: 'admin',
nickname: '管理员',
fixedUser: 'ENABLE',
},
currentTasks: null,
doorModule: {
@ -242,9 +238,6 @@ export const useSystemStore = defineStore('system', {
},
],
},
systemUser: {
username: '111',
},
errCraftList: [],
loginForm: {
username: import.meta.env.FT_NODE_ENV !== 'prod' ? 'admin' : '',
@ -275,9 +268,6 @@ export const useSystemStore = defineStore('system', {
updateMenuExpand() {
this.menuExpand = !this.menuExpand
},
updateSystemUser(data: System.SystemUser) {
this.systemUser = data
},
pushSystemList(text: any) {
this.systemList.push(text)
},
@ -288,5 +278,5 @@ export const useSystemStore = defineStore('system', {
})
},
},
persist: import.meta.env.FT_NODE_ENV === 'prod',
persist: ['prod', 'dev', 'test'].includes(import.meta.env.FT_NODE_ENV),
})

12
src/types/container.d.ts

@ -3,12 +3,12 @@ declare namespace Container {
id: number
createTime?: string
updateTime?: string
type: number
solutionId: number
pumpId: string
capacityTotal: string | number
capacityUsed: string | number
filled: string | number
type?: number
solutionId?: number
pumpId?: string
capacityTotal?: string | number
capacityUsed?: string | number
filled?: string | number
solutionName?: string
}
}

4
src/types/system.d.ts

@ -8,7 +8,6 @@ declare namespace System {
isDebug: boolean
errorCraft: boolean
menuExpand: boolean
systemUser: SystemUser
loginForm: LoginForm
systemLogList: SystemLog[]
currentTime: string
@ -91,9 +90,6 @@ declare namespace System {
command: string
params: T
}
interface SystemUser {
username: string
}
interface LoginForm {
username: string
password: string

3
src/types/user.d.ts

@ -5,7 +5,8 @@ declare namespace User {
updateTime?: string
username?: string
nickname?: string
password?: string | null
password?: string
passwordAgain?: string
role?: string
deleted?: string
fixedUser?: string

4
src/views/container/index.vue

@ -47,7 +47,7 @@ const queryContainerList = () => {
</script>
<template>
<div class="liquied-container">
<div class="liquid-container">
<liquidItem
v-for="(item, index) in chemicalList"
:key="item.id"
@ -59,7 +59,7 @@ const queryContainerList = () => {
</template>
<style>
.liquied-container {
.liquid-container {
display: grid;
grid-template-columns: repeat(4, auto);
grid-template-rows: repeat(1, auto);

21
src/views/debug/index.vue

@ -80,11 +80,11 @@ const commandHandle = async (command: string, params?: unknown) => {
|
el-form-item
ft-button(size="small", type="primary", :click-handle="() => commandHandle('robotic_arm_big_move_by', { ...debugStore.formData.transferModule.bigMotorData.relative, angle: debugStore.formData.transferModule.bigMotorData.relative.angle })")
|
|
ft-button(size="small", class="stop-button", :click-handle="() => commandHandle('robotic_arm_big_stop')")
| 停止
ft-button(size="small", type="primary", :click-handle="() => commandHandle('robotic_arm_big_move_by', { ...debugStore.formData.transferModule.bigMotorData.relative, angle: -(debugStore.formData.transferModule.bigMotorData.relative.angle ?? 0) })")
|
|
ft-button(type="primary", size="small", :click-handle="() => commandHandle('robotic_arm_big_origin')")
| 回原点
ft-button(size="small", type="primary", :click-handle="() => commandHandle('robotic_arm_big_enable')")
@ -121,17 +121,30 @@ const commandHandle = async (command: string, params?: unknown) => {
|
el-form-item
ft-button(size="small", type="primary", :click-handle="() => commandHandle('robotic_arm_small_move_by', { ...debugStore.formData.transferModule.smallMotorData.relative, distance: debugStore.formData.transferModule.smallMotorData.relative.angle })")
| 前进
| 左转
ft-button(size="small", class="stop-button", :click-handle="() => commandHandle('robotic_arm_small_stop')")
| 停止
ft-button(size="small", type="primary", :click-handle="() => commandHandle('robotic_arm_small_move_by', { ...debugStore.formData.transferModule.smallMotorData.relative, distance: -(debugStore.formData.transferModule.smallMotorData.relative.angle ?? 0) })")
| 后退
| 右转
ft-button(type="primary", size="small", :click-handle="() => commandHandle('robotic_arm_small_origin')")
| 回原点
ft-button(size="small", type="primary", :click-handle="() => commandHandle('robotic_arm_small_enable')")
| 电机使能
ft-button(size="small", :click-handle="() => commandHandle('robotic_arm_small_disable')")
| 电机失能
el-form-item(label="角度")
el-input(v-model="debugStore.formData.transferModule.smallMotorData.absolute.angle", type="number", placeholder="请输入目标角度")
template(#append)
| °
el-form-item(label="速度")
el-input(v-model="debugStore.formData.transferModule.smallMotorData.absolute.speed", type="number", placeholder="请输入速度")
template(#append)
| rpm/min
el-form-item
ft-button(size="small", type="primary", :click-handle="() => commandHandle('robotic_arm_small_move_to', debugStore.formData.transferModule.smallMotorData.absolute)")
| 开始
ft-button(size="small", class="stop-button", :click-handle="() => commandHandle('robotic_arm_small_stop')")
| 停止
el-divider Z轴电机
div.card-box
el-form

46
src/views/home/index.vue

@ -1,4 +1,5 @@
<script setup lang="ts">
import AddLiquid from 'components/home/AddLiquid/index.vue'
import { useSystemStore } from 'stores/systemStore'
import { watch } from 'vue'
@ -46,6 +47,8 @@ const systemStore = useSystemStore()
watch(() => systemStore.menuExpand, () => {
chartBox.value.resize()
})
const visible = ref(false)
</script>
<template lang="pug">
@ -74,17 +77,17 @@ watch(() => systemStore.menuExpand, () => {
el-col(:span="6")
div.home-right
div.right-photo
el-card
div.img-box
el-image(src="https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg" )
el-image(src="https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg" )
div.img-box
el-image(src="https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg" )
el-image(src="https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg" )
div.right-heat
el-card
template(#header)
span 加热位
div.content-box
div.heat-box
div.heat-box(:class="{'heat-box-active': true}")
p.title 加热位1
span.body 32
div.heat-box
p.title 加热位2
div.right-titration
@ -92,16 +95,17 @@ watch(() => systemStore.menuExpand, () => {
template(#header)
span 滴定位
div.content-box
div.titration-box
div.titration-box(:class="{'titration-box-active': true}")
p.title 滴定位1
div.titration-box
p.title 滴定位2
div.right-button
ft-button 手动滴定
ft-button.full-width(@click="visible = true") 手动滴定
ft-button 预充管路
ft-button 自动滴定
ft-button 清洗管路
ft-button 排空管路
AddLiquid(:visible="visible" @close="visible = false")
</template>
<style scoped lang="stylus">
@ -188,13 +192,20 @@ watch(() => systemStore.menuExpand, () => {
margin-bottom 5px
.home-right
display grid
grid-template-rows repeat(4, 1fr)
grid-template-rows 1fr 1fr 1fr 1fr
grid-gap 10px
.img-box
width 100%
height 100%
display flex
gap 10px
.right-button
display grid
grid-template-columns repeat(2, 1fr)
grid-template-rows repeat(3, 1fr)
gap 5px
.full-width
grid-column 1 / -1
:deep(.ft-button)
margin 0 !important
.my-button
@ -218,12 +229,6 @@ watch(() => systemStore.menuExpand, () => {
flex 1
padding 10px
overflow hidden
//display flex
.img-box
width 100%
height 100%
display flex
gap 10px
.content-box
//flex 1
height 100%
@ -231,14 +236,25 @@ watch(() => systemStore.menuExpand, () => {
display flex
justify-content space-between
align-items center
.heat-box-active, .titration-box-active
background: #12C290 !important
color: #fff !important
.heat-box, .titration-box
height 80px
width 80px
background: #EBEFF4
background #EBEFF4
border-radius 50%
border: 1px solid #fff
position relative
overflow hidden
display flex
flex-direction column
justify-content flex-start
align-items center
.body
margin-top 25 px
font-size 14px
font-weight 900
.title
position absolute
bottom 0

2
src/views/user/index.vue

@ -35,7 +35,7 @@ const columns = [
{
type: 'selection',
selectable: (row: any) => {
return row.fixedUser !== 'ENABLE'
return (row.fixedUser !== 'ENABLE') || row.username === systemStore.systemStatus.currentUser?.username
},
},
{

Loading…
Cancel
Save