Compare commits

..

5 commits

Author SHA1 Message Date
6188a55660 update 2024-06-08 18:19:37 +08:00
sysuid
98b56e09e7 Update Dockerfile
fix copy static file
2024-06-08 16:30:04 +08:00
sysuid
e624671c2a Update docker-compose.yml port mapping 2024-06-08 16:30:04 +08:00
sysuid
cf0e455007 Update Dockerfile
deploy static file into nginx image
2024-06-08 16:30:04 +08:00
78898a76bf update 2024-06-08 15:07:20 +08:00
7 changed files with 289 additions and 22 deletions

View file

@ -1,4 +1,4 @@
FROM node:22 FROM node:22 AS build-yarn
WORKDIR /app WORKDIR /app
@ -8,6 +8,10 @@ RUN yarn install
COPY . . COPY . .
EXPOSE 5173 RUN yarn build
CMD ["yarn", "dev", "--host", "0.0.0.0"] FROM nginx:latest AS runtime
COPY --from=build-yarn /app/dist/ /usr/share/nginx/html/
EXPOSE 80

View file

@ -5,7 +5,7 @@ services:
container_name: k5web container_name: k5web
network_mode: "bridge" network_mode: "bridge"
ports: ports:
- "5173:5173" - "5173:80"
environment: environment:
- TZ=Asia/Shanghai - TZ=Asia/Shanghai
restart: always restart: always

View file

@ -149,6 +149,7 @@ export default {
'global.password2': 'Retype password ', 'global.password2': 'Retype password ',
'image.negative': 'Negative', 'image.negative': 'Negative',
'workplace.clickNotice': ' (Official firmware can only detect 8KB/64Kbit)', 'workplace.clickNotice': ' (Official firmware can only detect 8KB/64Kbit)',
'menu.cps.radio': 'Radio',
...localeSettings, ...localeSettings,
...localeMessageBox, ...localeMessageBox,
...localeLogin, ...localeLogin,

View file

@ -149,6 +149,7 @@ export default {
'global.password2': '请再次输入密码', 'global.password2': '请再次输入密码',
'image.negative': '反色', 'image.negative': '反色',
'workplace.clickNotice': '(官方固件只能检测 8KB/64Kbit', 'workplace.clickNotice': '(官方固件只能检测 8KB/64Kbit',
'menu.cps.radio': '收音机',
...localeSettings, ...localeSettings,
...localeMessageBox, ...localeMessageBox,
...localeLogin, ...localeLogin,

View file

@ -32,6 +32,16 @@ const DASHBOARD: AppRouteRecordRaw = {
roles: ['*'], roles: ['*'],
}, },
}, },
{
path: 'radio',
name: 'Radio',
component: () => import('@/views/list/radio/index.vue'),
meta: {
locale: 'menu.cps.radio',
requiresAuth: true,
roles: ['*'],
},
},
{ {
path: 'settings', path: 'settings',
name: 'Settings', name: 'Settings',

View file

@ -9,30 +9,34 @@
<a-radio value="0" :disabled="state.disMatrix[0]['0']">英文信道</a-radio> <a-radio value="0" :disabled="state.disMatrix[0]['0']">英文信道</a-radio>
<a-radio value="4" :disabled="state.disMatrix[0]['4']">中文信道</a-radio> <a-radio value="4" :disabled="state.disMatrix[0]['4']">中文信道</a-radio>
</a-radio-group> </a-radio-group>
<a-radio-group v-model="state.flag[1]" type="button"> <a-radio-group v-model="state.flag[6]" type="button">
<a-radio value="0" :disabled="state.disMatrix[1]['0']">停用短信功能</a-radio> <a-radio value="0" :disabled="state.disMatrix[6]['0']">停用频谱仪</a-radio>
<a-radio value="1" :disabled="state.disMatrix[1]['1']">开启短信功能</a-radio> <a-radio value="1" :disabled="state.disMatrix[6]['1']">开启频谱仪</a-radio>
</a-radio-group>
<a-radio-group v-model="state.flag[3]" type="button">
<a-radio value="0" :disabled="state.disMatrix[3]['0']">停用MDC信令</a-radio>
<a-radio value="1" :disabled="state.disMatrix[3]['1']">开启MDC信令</a-radio>
</a-radio-group> </a-radio-group>
<a-radio-group v-model="state.flag[2]" type="button"> <a-radio-group v-model="state.flag[2]" type="button">
<a-radio value="0" :disabled="state.disMatrix[2]['0']">停用多普勒</a-radio> <a-radio value="0" :disabled="state.disMatrix[2]['0']">停用多普勒</a-radio>
<a-radio value="1" :disabled="state.disMatrix[2]['1']">开启多普勒</a-radio> <a-radio value="1" :disabled="state.disMatrix[2]['1']">开启多普勒</a-radio>
</a-radio-group> </a-radio-group>
<a-radio-group v-model="state.flag[3]" type="button"> <a-radio-group v-model="state.flag[5]" type="button">
<a-radio value="0" :disabled="state.disMatrix[3]['0']">停用MDC信令</a-radio> <a-radio value="0" :disabled="state.disMatrix[5]['0']">停用输入法</a-radio>
<a-radio value="1" :disabled="state.disMatrix[3]['1']">开启MDC信令</a-radio> <a-radio value="1" :disabled="state.disMatrix[5]['1']">开启输入法</a-radio>
</a-radio-group>
<a-radio-group v-model="state.flag[1]" type="button">
<a-radio value="0" :disabled="state.disMatrix[1]['0']">停用短信功能</a-radio>
<a-radio value="1" :disabled="state.disMatrix[1]['1']">开启短信功能</a-radio>
</a-radio-group> </a-radio-group>
<a-radio-group v-model="state.flag[4]" type="button"> <a-radio-group v-model="state.flag[4]" type="button">
<a-radio value="0" :disabled="state.disMatrix[4]['0']">停用收音机</a-radio> <a-radio value="0" :disabled="state.disMatrix[4]['0']">停用收音机</a-radio>
<a-radio value="F" :disabled="state.disMatrix[4]['F']">默认收音机</a-radio> <a-radio value="F" :disabled="state.disMatrix[4]['F']">默认收音机</a-radio>
<a-radio value="4" :disabled="state.disMatrix[4]['4']">SI4732收音机</a-radio> <a-radio value="4" :disabled="state.disMatrix[4]['4']">SI4732收音机</a-radio>
</a-radio-group> </a-radio-group>
<a-radio-group v-model="state.flag[5]" type="button"> <a-radio-group v-model="state.flag[7]" type="button">
<a-radio value="0" :disabled="state.disMatrix[5]['0']">停用输入法</a-radio> <a-radio value="0" :disabled="state.disMatrix[7]['0']">停用 SI4732 单边带</a-radio>
<a-radio value="1" :disabled="state.disMatrix[5]['1']">开启输入法</a-radio> <a-radio value="1" :disabled="state.disMatrix[7]['1']">开启 SI4732 单边带</a-radio>
</a-radio-group>
<a-radio-group v-model="state.flag[6]" type="button">
<a-radio value="0" :disabled="state.disMatrix[6]['0']">停用频谱仪</a-radio>
<a-radio value="1" :disabled="state.disMatrix[6]['1']">开启频谱仪</a-radio>
</a-radio-group> </a-radio-group>
<a-button type="primary" @click="useFirmware">生成</a-button> <a-button type="primary" @click="useFirmware">生成</a-button>
</t-space> </t-space>
@ -52,7 +56,7 @@
router.push({ router.push({
path: '/tool/flash', path: '/tool/flash',
query: { query: {
url: '/diy/LOSEHU' + state.flag.join('') + '.bin' url: 'https://k5.vicicode.com/diyapi/LOSEHU' + state.flag.join('') + '.bin'
} }
}); });
} }
@ -63,7 +67,7 @@
disMatrix: any disMatrix: any
} = reactive({ } = reactive({
versions: [], versions: [],
flag: ['0','0','0','0','0','0','0'], flag: ['0','0','0','0','0','0','0','0'],
disMatrix: [ disMatrix: [
{0: false, 4: false}, {0: false, 4: false},
{0: false, 1: false}, {0: false, 1: false},
@ -72,10 +76,13 @@
{0: false, F: false, 4: false}, {0: false, F: false, 4: false},
{0: false, 1: false}, {0: false, 1: false},
{0: false, 1: false}, {0: false, 1: false},
{0: false, 1: false},
] ]
}) })
watch(state.flag, ()=>{ watch(state.flag, ()=>{ updateMatrix() })
const updateMatrix = () => {
state.flag.map((e: any,i: any)=>{ state.flag.map((e: any,i: any)=>{
Object.keys(state.disMatrix[i]).map((ex: any)=>{ Object.keys(state.disMatrix[i]).map((ex: any)=>{
if(state.versions.indexOf('LOSEHU' + state.flag.join('').substring(0, i) + ex + state.flag.join('').substring(i+1) + '.bin') == -1){ if(state.versions.indexOf('LOSEHU' + state.flag.join('').substring(0, i) + ex + state.flag.join('').substring(i+1) + '.bin') == -1){
@ -85,11 +92,12 @@
} }
}) })
}) })
}) }
onMounted(async ()=>{ onMounted(async ()=>{
const versions = await (await fetch('/diy/version.json')).text() const versions = await (await fetch('https://k5.vicicode.com/diyapi/version.json?v=' + (new Date()).getTime())).text()
state.versions = JSON.parse(versions) state.versions = JSON.parse(versions)
updateMatrix()
}) })
</script> </script>

View file

@ -0,0 +1,243 @@
<template>
<div class="container">
<Breadcrumb :items="[$t('menu.dashboard'), $t('menu.cps.radio')]" />
<a-card class="general-card">
<template #title>
<span @click="()=>{istate.showHide += 1}">{{ $t('menu.cps.radio') + $t('global.onStart') }}</span>
</template>
<a-row style="margin-bottom: 16px">
<a-col :span="12">
<a-space>
<a-button type="primary" @click="readChannel">
<template #icon>
<icon-plus />
</template>
{{ $t('cps.onDeviceRead') }}
</a-button>
<a-button @click="writeChannel">
<template #icon>
<icon-plus />
</template>
{{ $t('cps.onDeviceWrite') }}
</a-button>
</a-space>
</a-col>
<!-- <a-col :span="12" style="text-align: right;">
<a-space>
<a-button type="text" @click="downloadExcelTemplate">
{{ $t('cps.downloadImportTemplate') }}
</a-button>
<a-button type="primary" @click="restoreExcelChannel">
{{ $t('cps.import') }}
</a-button>
<a-button @click="saveExcelChannel">
{{ $t('cps.export') }}
</a-button>
</a-space>
</a-col> -->
</a-row>
<t-table
class="ttable"
:loading="loading"
size="medium"
:columns="columns"
:data="cstate.renderData"
:pagination="{
defaultPageSize: cstate.pageSize,
total: cstate.renderData.length,
defaultCurrent: 1,
pageSizeOptions: [15, 30, 50, 100, 200]
}"
@change="(e: any)=>{cstate.pageSize = e.pagination.pageSize, cstate.nowPage = e.pagination.current}"
bordered
lazy-load
:headerAffixedTop="{ offsetTop: 60 }"
:hover="true"
drag-sort="row-handler"
@drag-sort="onDragSort"
>
<template #drag="{ row, rowIndex }">
<span>
<MoveIcon />
</span>
</template>
<template #index="{ row, rowIndex }">
{{ (cstate.nowPage - 1) * cstate.pageSize + rowIndex + 1 }}
</template>
<template #operate="{ row, rowIndex }">
<t-button theme="default" variant="dashed" @click="clearRow((cstate.nowPage - 1) * cstate.pageSize + rowIndex)">{{ $t('cps.clear') }}</t-button>
</template>
</t-table>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { computed, reactive } from 'vue';
import { Input } from 'tdesign-vue-next';
import useLoading from '@/hooks/loading';
import { eeprom_read, uint8ArrayToHexReverseString, hexReverseStringToUint8Array, eeprom_write, eeprom_reboot, eeprom_init } from '@/utils/serial.js';
import { useAppStore } from '@/store';
import { MoveIcon } from 'tdesign-icons-vue-next';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const appStore = useAppStore();
const { loading, setLoading } = useLoading(false);
const cstate : {
renderData: any[],
pageSize: number,
nowPage: number
} = reactive({
renderData: Array.from({length: 20}).map(e=>{return {scanlist: []}}),
pageSize: 20,
nowPage: 1,
})
const istate = reactive({
showHide: 0
})
const onDragSort = (params: any) => {
cstate.renderData = params.newData
}
const columns = computed(() => [
{
colKey: 'drag', //
title: t('cps.sort'),
width: 46,
},
{
title: '#',
colKey: 'index',
align: 'left',
width: 100
},
{
title: t('cps.rx'),
colKey: 'rx',
align: 'left',
width: 200,
cell: (h, { row }) => parseFloat(row.rx) ? parseFloat(row.rx).toFixed(2) : undefined,
edit: {
component: Input,
props: {
clearable: true
},
onEdited: (context: any) => {
context.newRowData.rx = context.newRowData.rx ? context.newRowData.rx : undefined
const newData = [...cstate.renderData];
newData.splice((cstate.nowPage - 1) * cstate.pageSize + context.rowIndex, 1, context.newRowData);
cstate.renderData = newData;
},
},
},
{
title: t('cps.operate'),
colKey: 'operate',
align: 'left',
width: 150
}
]);
const readChannel = async() => {
if(appStore.connectState != true){alert(sessionStorage.getItem('noticeConnectK5')); return;};
await eeprom_init(appStore.connectPort);
setLoading(true)
let rawEEPROM = new Uint8Array(0x028);
for (let i = 0x0E40; i < 0x0E61; i += 0x08) {
const _data = await eeprom_read(appStore.connectPort, i, 0x08, appStore.configuration?.uart)
rawEEPROM.set(_data, i - 0x0E40)
}
const _renderData : any = [];
for (let i = 0; i < 0x028; i += 0x02) {
const rx = uint8ArrayToHexReverseString(rawEEPROM.subarray(i, i + 0x02))
if(rx != 'ffff'){
_renderData.push({
rx: parseInt(rx, 16) / 10
})
}else{
_renderData.push({})
}
}
cstate.renderData = _renderData;
console.log(rawEEPROM)
setLoading(false)
}
const writeChannel = async() =>{
if(appStore.connectState != true){alert(sessionStorage.getItem('noticeConnectK5')); return;};
await eeprom_init(appStore.connectPort);
setLoading(true)
for (let i = 0; i < 0x028; i += 0x02) {
if(cstate.renderData[i / 0x02].rx){
await eeprom_write(appStore.connectPort, i + 0x0E40, hexReverseStringToUint8Array((parseInt(cstate.renderData[i / 0x02].rx * 10)).toString(16).padStart(4, '0')), 0x02, appStore.configuration?.uart);
}else{
await eeprom_write(appStore.connectPort, i + 0x0E40, hexReverseStringToUint8Array('0000'), 0x02, appStore.configuration?.uart);
}
}
await eeprom_reboot(appStore.connectPort);
setLoading(false)
}
const clearRow = async (row: any) =>{
const newData = [...cstate.renderData];
newData.splice(row, 1, {scanlist: []});
cstate.renderData = newData;
}
</script>
<script lang="ts">
export default {
name: 'Radio',
};
</script>
<style scoped lang="less">
:deep(::-webkit-scrollbar-thumb){
border-radius: 0 !important;
}
:deep(.scrollbar::-webkit-scrollbar) {
height: 10px;
}
:deep(.t-table__content::-webkit-scrollbar) {
height: 15px;
}
.container {
padding: 0 20px 20px 20px;
}
:deep(.arco-table-th) {
&:last-child {
.arco-table-th-item-title {
margin-left: 16px;
}
}
}
.action-icon {
margin-left: 12px;
cursor: pointer;
}
.active {
color: #0960bd;
background-color: #e3f4fc;
}
.setting {
display: flex;
align-items: center;
width: 200px;
.title {
margin-left: 12px;
cursor: pointer;
}
}
.ttable {
:deep(.t-table__affixed-header-elm-wrap){
height: 60px !important;
}
:deep(.t-table__content){
scrollbar-width: auto !important;
}
}
</style>