mirror of
https://github.com/silenty4ng/k5web
synced 2025-01-15 08:15:18 +00:00
Compare commits
5 commits
266c53c2af
...
6188a55660
Author | SHA1 | Date | |
---|---|---|---|
6188a55660 | |||
|
98b56e09e7 | ||
|
e624671c2a | ||
|
cf0e455007 | ||
78898a76bf |
7 changed files with 289 additions and 22 deletions
10
Dockerfile
10
Dockerfile
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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>
|
||||||
|
|
243
src/views/list/radio/index.vue
Normal file
243
src/views/list/radio/index.vue
Normal 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>
|
||||||
|
|
Loading…
Reference in a new issue