mirror of
https://github.com/silenty4ng/k5web
synced 2025-01-28 14:42:43 +00:00
Merge branch 'silenty4ng:master' into master
This commit is contained in:
commit
9246d91aeb
30 changed files with 1324 additions and 16979 deletions
|
@ -7,6 +7,12 @@
|
|||
|
||||
K5Web 用于对兼容业余无线电台 UV-K5 写频、更新固件、写入星历等。
|
||||
|
||||
## 讨论
|
||||
- QQ 群:957225277 (K5Web相关)
|
||||
- QQ 群:201308015 (固件相关)
|
||||
- Telegram Group: https://t.me/losehu
|
||||
- Matrix Group: https://matrix.to/#/#losehu:mozilla.org
|
||||
|
||||
## 功能列表
|
||||
|
||||
- 固件版本检测
|
||||
|
@ -19,6 +25,9 @@ K5Web 用于对兼容业余无线电台 UV-K5 写频、更新固件、写入星
|
|||
- 开机图片(LOSEHU 固件)
|
||||
- 字库写入(LOSEHU 固件)
|
||||
- 星历写入(LOSEHU 固件)
|
||||
- DTMF ID 设置
|
||||
- 收音机频道管理
|
||||
- MDC 联系人管理(LOSEHU 固件)
|
||||
|
||||
## 开发
|
||||
### 安装依赖
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<html lang="zh-cmn">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>K5Web</title>
|
||||
<style>
|
||||
|
|
16928
package-lock.json
generated
16928
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -37,6 +37,7 @@
|
|||
"vue": "^3.2.40",
|
||||
"vue-echarts": "^6.2.3",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-matomo": "^4.2.0",
|
||||
"vue-router": "^4.0.14",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
|
|
BIN
public/gy.png
BIN
public/gy.png
Binary file not shown.
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 44 KiB |
BIN
public/new_font_k_f.bin
Normal file
BIN
public/new_font_k_f.bin
Normal file
Binary file not shown.
11
src/App.vue
11
src/App.vue
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div v-if="isWeixin || isQQ" style="text-align: center;">
|
||||
<div v-if="(isWeixin || isQQ) && route.path !== '/satloc'" style="text-align: center;">
|
||||
<div style="height: 75vh; display: flex; flex-direction: column; align-items: center;">
|
||||
<div style="padding: 20px; padding-top: 35vh; font-size: 1.5rem;">如需浏览,请长按网址复制后使用浏览器访问</div>
|
||||
<p style="padding: 20px; background-color: #F1F1F1;">{{ link }}</p>
|
||||
</div>
|
||||
<div style="color: #AAAAAA;">{{ ua }}</div>
|
||||
</div>
|
||||
<t-config-provider v-if="reloadLang && !isWeixin && !isQQ" :global-config="locale">
|
||||
<t-config-provider v-if="reloadLang && !((isWeixin || isQQ) && route.path !== '/satloc')" :global-config="locale">
|
||||
<a-config-provider :locale="locale">
|
||||
<router-view />
|
||||
<global-setting />
|
||||
|
@ -25,6 +25,9 @@
|
|||
import Aegis from 'aegis-web-sdk';
|
||||
import { encodingIndexes } from "@zxing/text-encoding/es2015/encoding-indexes";
|
||||
import { TextEncoder, TextDecoder } from "@zxing/text-encoding";
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
window.TextEncodingIndexes = { encodingIndexes: encodingIndexes };
|
||||
window.TextEncoder = TextEncoder;
|
||||
|
@ -39,10 +42,6 @@
|
|||
spa: true, // spa 应用页面跳转的时候开启 pv 计算
|
||||
hostUrl: 'https://rumt-zh.com'
|
||||
});
|
||||
const shynet = document.createElement('script');
|
||||
shynet.defer;
|
||||
shynet.src = "https://shynet.vicicode.com/ingress/4c1dcea4-75c5-45e2-a641-25f211adbad6/script.js";
|
||||
document.body.append(shynet);
|
||||
}
|
||||
|
||||
const { currentLocale } = useLocale();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<a-layout-footer class="footer">
|
||||
<a href="https://github.com/silenty4ng/k5web" target="_blank">K5Web - V0.1.202405130020</a>
|
||||
<t-link href="https://github.com/silenty4ng/k5web" target="_blank">K5Web - V0.1.202406100220</t-link>
|
||||
</a-layout-footer>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<t-link href="https://txc.qq.com/products/647342" target="_blank">{{ $t('navbar.qa') }}</t-link>
|
||||
</li>
|
||||
<li>
|
||||
<a-button type="primary" @click="connectIt">{{ appStore.connectState ? $t('navbar.disconnect') : $t('navbar.connect') }}</a-button>
|
||||
<a-button v-show="route.path !== '/tool/flash'" type="primary" @click="connectIt">{{ appStore.connectState ? $t('navbar.disconnect') : $t('navbar.connect') }}</a-button>
|
||||
</li>
|
||||
<li>
|
||||
<a-tooltip :content="$t('settings.language')">
|
||||
|
@ -226,6 +226,11 @@
|
|||
const configuration_list : any = {
|
||||
"LOSEHU.*P.*K" : "ltsk.json",
|
||||
"LOSEHU.*P.*" : "lts.json",
|
||||
"LOSEHU13[0-9].*HS" : "losehu124h.json",
|
||||
"LOSEHU13[0-9].*H" : "losehu124h.json",
|
||||
"LOSEHU13[0-9].*KS" : "losehu120k.json",
|
||||
"LOSEHU13[0-9].*K" : "losehu120k.json",
|
||||
"LOSEHU13[0-9].*" : "losehu118.json",
|
||||
"LOSEHU12[4-9].*HS" : "losehu124h.json",
|
||||
"LOSEHU12[4-9].*H" : "losehu124h.json",
|
||||
"LOSEHU12[0-3].*H" : "losehu118h.json",
|
||||
|
@ -282,7 +287,8 @@
|
|||
"H": false,
|
||||
"localmdc": false,
|
||||
"sat": false,
|
||||
"newpinyin": false
|
||||
"newpinyin": false,
|
||||
'fm30': false
|
||||
}
|
||||
|
||||
Object.keys(configuration_list).some(e=>{
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
"uart": "losehu",
|
||||
"charset": "losehu",
|
||||
"K": true,
|
||||
"localmdc": true
|
||||
"localmdc": true,
|
||||
"fm30": true
|
||||
}
|
|
@ -112,9 +112,11 @@ export default {
|
|||
'tool.flash': 'FLASH',
|
||||
'tool.selectImage': 'Select Image',
|
||||
'tool.write': 'Write to device',
|
||||
'tool.fontwrite': 'LOSEHU Firmware Character Set Write',
|
||||
'tool.pinyinwrite': 'LOSEHU H Firmware Pinyin Set Write',
|
||||
'tool.fontwrite': 'LOSEHU Chinese Character Set Write',
|
||||
'tool.pinyinwrite': 'LOSEHU H Chinese Pinyin Set Write',
|
||||
'tool.writefontwrite': 'Character Set Write',
|
||||
'tool.Simplified_Chinese': 'CHS',
|
||||
'tool.Traditional_Chinese': 'CHT',
|
||||
'tool.writepinyin': 'Pinyin Set Write',
|
||||
'tool.brtime': 'Browser Time',
|
||||
'tool.selectSatellite': 'Select satellite',
|
||||
|
@ -137,7 +139,8 @@ export default {
|
|||
'global.nosupport': 'Current browser does not support WebSerial function, please use Chrome, Edge, Opera browser.',
|
||||
'global.connectFail': 'Connect Fail',
|
||||
'menu.workshop': 'Workshop',
|
||||
'menu.firmware': 'Firmware',
|
||||
'menu.firmware': 'Firmware Store',
|
||||
'menu.channel': 'Channel Share',
|
||||
'global.use': 'Use',
|
||||
'tool.ssbpatch': 'LOSEHU S Firmware SI4732 SSB Patch',
|
||||
'tool.writessbpatch': 'SSB Patch Write',
|
||||
|
@ -149,6 +152,13 @@ export default {
|
|||
'global.password2': 'Retype password ',
|
||||
'image.negative': 'Negative',
|
||||
'workplace.clickNotice': ' (Official firmware can only detect 8KB/64Kbit)',
|
||||
'menu.cps.radio': 'Radio',
|
||||
'menu.cps.mdc': 'MDC Contact',
|
||||
'cps.contact': 'Name',
|
||||
'cps.mdcid': 'MDC ID',
|
||||
'idea.diy': 'LOSEHU DIY',
|
||||
'diy.generate': 'Generate',
|
||||
'cps.dtmfid': 'DTMF ID',
|
||||
...localeSettings,
|
||||
...localeMessageBox,
|
||||
...localeLogin,
|
||||
|
|
|
@ -115,6 +115,8 @@ export default {
|
|||
'tool.fontwrite': 'LOSEHU 固件字库写入',
|
||||
'tool.pinyinwrite': 'LOSEHU H 版固件拼音索引表',
|
||||
'tool.writefontwrite': '自动写入字库',
|
||||
'tool.Simplified_Chinese': '简体',
|
||||
'tool.Traditional_Chinese': '繁体',
|
||||
'tool.writepinyin': '写入拼音检索表',
|
||||
'tool.brtime': '浏览器时间',
|
||||
'tool.selectSatellite': '选择卫星',
|
||||
|
@ -138,6 +140,7 @@ export default {
|
|||
'global.connectFail': '连接失败',
|
||||
'menu.workshop': '创意工坊',
|
||||
'menu.firmware': '固件市场',
|
||||
'menu.channel': '信道分享',
|
||||
'global.use': '使用',
|
||||
'tool.ssbpatch': 'LOSEHU S 版固件 SI4732 单边带补丁',
|
||||
'tool.writessbpatch': '写入单边带补丁',
|
||||
|
@ -149,6 +152,13 @@ export default {
|
|||
'global.password2': '请再次输入密码',
|
||||
'image.negative': '反色',
|
||||
'workplace.clickNotice': '(官方固件只能检测 8KB/64Kbit)',
|
||||
'menu.cps.radio': '收音机',
|
||||
'menu.cps.mdc': 'MDC 联系人',
|
||||
'cps.contact': '联系人',
|
||||
'cps.mdcid': 'MDC ID',
|
||||
'idea.diy': '自定义萝卜固件',
|
||||
'diy.generate': '生成',
|
||||
'cps.dtmfid': 'DTMF ID',
|
||||
...localeSettings,
|
||||
...localeMessageBox,
|
||||
...localeLogin,
|
||||
|
|
20
src/main.ts
20
src/main.ts
|
@ -13,6 +13,18 @@ import App from './App.vue';
|
|||
import '@/assets/style/global.less';
|
||||
import '@/api/interceptor';
|
||||
import 'tdesign-vue-next/es/style/index.css';
|
||||
import Updater from "./utils/AutoUpdate.js";
|
||||
import VueMatomo from 'vue-matomo';
|
||||
|
||||
const AutoUpdate = new Updater()
|
||||
AutoUpdate.on('update',()=>{
|
||||
setTimeout(async()=>{
|
||||
const result = confirm('当前网站有更新,请点击确定刷新页面体验');
|
||||
if(result){
|
||||
location.reload();
|
||||
}
|
||||
},500)
|
||||
})
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
|
@ -24,4 +36,12 @@ app.use(i18n);
|
|||
app.use(globalComponents);
|
||||
app.use(directive);
|
||||
|
||||
if(location.hostname == 'k5.vicicode.com' || location.hostname == 'k5.lhw711.cn' || location.hostname == 'mm.md' || location.hostname == 'k5.mm.md'){
|
||||
app.use(VueMatomo, {
|
||||
host: '//analytics.vicicode.com',
|
||||
siteId: 2,
|
||||
router: router
|
||||
})
|
||||
}
|
||||
|
||||
app.mount('#app');
|
||||
|
|
|
@ -32,6 +32,26 @@ const DASHBOARD: AppRouteRecordRaw = {
|
|||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'radio',
|
||||
name: 'Radio',
|
||||
component: () => import('@/views/list/radio/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.cps.radio',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'mdc',
|
||||
name: 'Mdc',
|
||||
component: () => import('@/views/list/mdc/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.cps.mdc',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
name: 'Settings',
|
||||
|
|
|
@ -31,7 +31,27 @@ const IDEA: AppRouteRecordRaw = {
|
|||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'channel',
|
||||
name: 'ideaChannel',
|
||||
component: () => import('@/views/idea/channel/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.channel',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'losehu',
|
||||
name: 'ideaLosehu',
|
||||
component: () => import('@/views/idea/losehu/index.vue'),
|
||||
meta: {
|
||||
locale: 'idea.diy',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
66
src/utils/AutoUpdate.js
Normal file
66
src/utils/AutoUpdate.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
/**
|
||||
* 前端重新部署通知用户刷新网页
|
||||
*/
|
||||
|
||||
class Updater {
|
||||
oldScript = []; // 存储第一次值也就是script 的hash 信息
|
||||
newScript = []; // 获取新的值 也就是新的script 的hash信息
|
||||
dispatch = {}; // 小型发布订阅通知用户更新了
|
||||
|
||||
constructor() {
|
||||
this.oldScript = [];
|
||||
this.newScript = [];
|
||||
this.dispatch = {};
|
||||
this.init(); // 初始化
|
||||
this.timing();
|
||||
}
|
||||
|
||||
async init() {
|
||||
const html = await this.getHtml();
|
||||
this.oldScript = this.parserScript(html);
|
||||
};
|
||||
|
||||
async getHtml() {
|
||||
const html = await fetch('/').then(res => res.text());//读取index html
|
||||
return html
|
||||
};
|
||||
|
||||
parserScript(html) {
|
||||
const reg = new RegExp(/<script(?:\s+[^>]*)?>(.*?)<\/script\s*>/ig) //script正则
|
||||
return html.match(reg) //匹配script标签
|
||||
}
|
||||
|
||||
//发布订阅通知
|
||||
on(key, fn) {
|
||||
(this.dispatch[key] || (this.dispatch[key] = [])).push(fn)
|
||||
return this;
|
||||
}
|
||||
|
||||
compare(oldArr, newArr) {
|
||||
const base = oldArr.length;
|
||||
const arr = Array.from(new Set(oldArr.concat(newArr)));
|
||||
//如果新旧length 一样无更新
|
||||
if (arr.length === base) {
|
||||
// this.dispatch['no-update'].forEach(fn => {
|
||||
// fn();
|
||||
// })
|
||||
} else {
|
||||
// 否则通知更新
|
||||
this.dispatch['update'].forEach(fn => {
|
||||
fn();
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
async timing() {
|
||||
setInterval(async () => {
|
||||
const newHtml = await this.getHtml();
|
||||
this.newScript = this.parserScript(newHtml);
|
||||
this.compare(this.oldScript, this.newScript)
|
||||
//这边给的是默认值15000,也可以自定义秒数
|
||||
}, 15000);
|
||||
};
|
||||
}
|
||||
|
||||
export default Updater;
|
|
@ -14,7 +14,16 @@
|
|||
</a-card>
|
||||
</a-space>
|
||||
<div>
|
||||
<img style="margin-bottom: 10px;" width="600px" src="/gy.png" />
|
||||
<a-typography-title :heading="5">说明:</a-typography-title>
|
||||
<a-typography-text>◆ 使用应第一时间<t-link theme="primary" href="/#/tool/backup">备份</t-link>配置及校准数据。</a-typography-text><br>
|
||||
<a-typography-text>◆ 除“固件升级”功能手台应处于刷机模式点击更新按钮选择设备更新,其余功能手台均需要在正常模式连接。</a-typography-text><br>
|
||||
<a-typography-text>◆ 萝卜(LOSEHU)固件相关问题请移步:<t-link theme="primary" href="https://github.com/losehu/uv-k5-firmware-custom" target="_blank">https://github.com/losehu/uv-k5-firmware-custom</t-link> 。</a-typography-text><br>
|
||||
<a-typography-text>◆ K5Web 使用视频教程(BG7QJV):<t-link theme="primary" href="https://www.douyin.com/video/7378314511419313458" target="_blank">https://www.douyin.com/video/7378314511419313458</t-link> 。</a-typography-text><br>
|
||||
<a-typography-text>◆ K5Web 使用视频教程(BG3ODZ):<t-link theme="primary" href="https://www.bilibili.com/video/BV1Q4421D75x" target="_blank">https://www.bilibili.com/video/BV1Q4421D75x</t-link> 。</a-typography-text>
|
||||
</div>
|
||||
<div>
|
||||
<a-typography-title :heading="5">希望工程1+1助学行动:</a-typography-title>
|
||||
<img class="tencent" style="margin-bottom: 10px;" width="200px" src="/gy.png" />
|
||||
</div>
|
||||
</a-col>
|
||||
</template>
|
||||
|
@ -102,4 +111,10 @@
|
|||
:deep(.arco-icon-home) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
body[arco-theme='dark'] {
|
||||
.tencent {
|
||||
filter: invert(1) hue-rotate(180deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
263
src/views/idea/channel/index.vue
Normal file
263
src/views/idea/channel/index.vue
Normal file
|
@ -0,0 +1,263 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="[$t('menu.workshop'), $t('menu.channel')]" />
|
||||
<a-row :gutter="20" align="stretch">
|
||||
<a-col :span="24">
|
||||
<a-card class="general-card" :title="$t('menu.channel')">
|
||||
<template #extra>
|
||||
<div style="margin-right: 20px;">
|
||||
<template v-if="userStore.name">
|
||||
<a-link @click="showPanel"> {{ userStore.name }} </a-link>
|
||||
<a-link @click="userStore.logout()"> {{ $t('global.logout') }} </a-link>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-link @click="userStore.setInfo({ showLogin: true })"> {{ $t('global.login') }} </a-link>
|
||||
<a-link @click="userStore.setInfo({ showRegister: true })"> {{ $t('global.register') }} </a-link>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<a-list>
|
||||
<a-list-item style="width: 100%;" v-for="item in state.nowpage">
|
||||
<a-list-item-meta
|
||||
:description="item.desc"
|
||||
>
|
||||
<template #title>
|
||||
<t-tag theme="primary" variant="outline">{{ item.upload }}</t-tag> {{ item.title }}
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
<template #actions>
|
||||
<a-link @click="onStar(item.id)">👍</a-link>
|
||||
<a-link @click="useFirmware('https://k5.vicicode.com/wsapi/download?id=' + item.id)">{{$t('global.use')}}</a-link>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
<t-pagination @change="loadit" style="margin: 10px;" :total="state.total" :current="state.page" :pageSize="12" showPageNumber :showPageSize="false" />
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<t-drawer v-model:visible="state.showPanel" size="50%" header="我的分享" :footer="false">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<t-button style="margin: 10px" @click="showUpload">上传新分享</t-button>
|
||||
<t-button :loading="state.refLoading" shape="circle" theme="outline" @click="refit">
|
||||
<template #icon> <RefreshIcon /> </template>
|
||||
</t-button>
|
||||
</div>
|
||||
<t-list :split="true">
|
||||
<t-list-item v-for="item in state.myList">
|
||||
<div style="display: flex; width: 100%;">
|
||||
<div style="width: 90%;">
|
||||
<t-tag theme="primary" variant="outline">{{ item.audit ? '已审核' : '审核中' }}</t-tag>
|
||||
{{ item.title }}
|
||||
<br>
|
||||
{{ item.desc }}
|
||||
</div>
|
||||
<div style="width: 10%; margin: auto; text-align: center;">
|
||||
<t-link theme="primary" hover="color" @click="onDT(item.id)">删除</t-link>
|
||||
</div>
|
||||
</div>
|
||||
</t-list-item>
|
||||
</t-list>
|
||||
</t-drawer>
|
||||
<t-drawer v-model:visible="state.showUpload" size="25%" header="上传新固件" :footer="false">
|
||||
<t-form
|
||||
:data="formData"
|
||||
reset-type="initial"
|
||||
colon
|
||||
@submit="onUF"
|
||||
>
|
||||
<t-form-item label="分享名称" name="title" label-align="top">
|
||||
<t-input v-model="formData.title"></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="分享描述" name="desc" label-align="top">
|
||||
<t-textarea :autosize="{ minRows: 5, maxRows: 10 }" v-model="formData.desc" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="信道文件" name="firmware" label-align="top">
|
||||
<t-upload
|
||||
v-model="formData.firmware"
|
||||
action="https://k5.vicicode.com/wsapi/base64"
|
||||
:abridge-name="[8, 6]"
|
||||
theme="file-input"
|
||||
placeholder="未选择文件"
|
||||
></t-upload>
|
||||
</t-form-item>
|
||||
<t-form-item label-align="top">
|
||||
<t-button theme="primary" type="submit" block>提交审核</t-button>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</t-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, nextTick, onMounted, watch } from 'vue';
|
||||
import { useAppStore, useUserStore } from '@/store';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { RefreshIcon } from 'tdesign-icons-vue-next';
|
||||
import axios from 'axios';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const state : {
|
||||
binaryFile: any,
|
||||
loading: boolean,
|
||||
showPanel: boolean,
|
||||
showUpload: boolean,
|
||||
refLoading: boolean,
|
||||
myList: any,
|
||||
total: number,
|
||||
page: number,
|
||||
nowpage: any
|
||||
} = reactive({
|
||||
binaryFile: undefined,
|
||||
loading: false,
|
||||
showPanel: false,
|
||||
showUpload: false,
|
||||
refLoading: false,
|
||||
myList: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
nowpage: []
|
||||
})
|
||||
|
||||
const formData = reactive({
|
||||
title: '',
|
||||
desc: '',
|
||||
firmware: []
|
||||
})
|
||||
|
||||
onMounted(async ()=>{
|
||||
loadit({current: 1})
|
||||
})
|
||||
|
||||
const loadit = async (page: any) => {
|
||||
state.page = page.current
|
||||
const resp : any = await axios.get("https://k5.vicicode.com/wsapi/list?type=2&limit=12&page=" + page.current + "&t=" + Date.now())
|
||||
state.total = resp.total
|
||||
state.nowpage = resp.data
|
||||
}
|
||||
|
||||
const showPanel = async () => {
|
||||
state.refLoading = true;
|
||||
state.showPanel = true
|
||||
const resp : any = await axios.post("https://k5.vicicode.com/wsapi/my_list", {
|
||||
'type': 2,
|
||||
'token': userStore.accountId
|
||||
})
|
||||
state.myList = resp.data
|
||||
state.refLoading = false;
|
||||
}
|
||||
|
||||
const showUpload = () => {
|
||||
formData.title = ''
|
||||
formData.desc = ''
|
||||
formData.firmware = []
|
||||
state.showUpload = true
|
||||
}
|
||||
|
||||
const onUF = async () => {
|
||||
if(formData.title == "" || formData.firmware.length == 0){
|
||||
Message.error({
|
||||
content: '未填写名称及上传文件',
|
||||
duration: 5 * 1000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await axios.post("https://k5.vicicode.com/wsapi/upload", {
|
||||
'type': 2,
|
||||
'token': userStore.accountId,
|
||||
'title': formData.title,
|
||||
'desc': formData.desc,
|
||||
'data': formData.firmware[0].url
|
||||
})
|
||||
state.showUpload = false;
|
||||
showPanel()
|
||||
}
|
||||
|
||||
const onDT = async (id: any) => {
|
||||
await axios.post("https://k5.vicicode.com/wsapi/delete", {
|
||||
'id': id,
|
||||
'token': userStore.accountId,
|
||||
})
|
||||
showPanel()
|
||||
}
|
||||
|
||||
const onStar = async (id: any) => {
|
||||
await axios.post("https://k5.vicicode.com/wsapi/star", {
|
||||
'id': id
|
||||
})
|
||||
Message.success({
|
||||
content: '点赞成功',
|
||||
duration: 5 * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
const refit = () => {
|
||||
showPanel()
|
||||
}
|
||||
|
||||
const useFirmware = (url: string) => {
|
||||
router.push({
|
||||
path: '/chirp/channel',
|
||||
query: {
|
||||
url
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Backup',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
padding: 0 20px 20px 20px;
|
||||
:deep(.arco-list-content) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:deep(.arco-card-meta-title) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
:deep(.arco-list-col) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
:deep(.arco-list-item) {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
:deep(.block-title) {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
:deep(.list-wrap) {
|
||||
// min-height: 140px;
|
||||
.list-row {
|
||||
align-items: stretch;
|
||||
.list-col {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
:deep(.arco-space) {
|
||||
width: 100%;
|
||||
.arco-space-item {
|
||||
&:last-child {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -19,9 +19,11 @@
|
|||
<a-list>
|
||||
<a-list-item style="width: 100%;" v-for="item in state.nowpage">
|
||||
<a-list-item-meta
|
||||
:title="item.title"
|
||||
:description="item.desc"
|
||||
>
|
||||
<template #title>
|
||||
<t-tag theme="primary" variant="outline">{{ item.upload }}</t-tag> {{ item.title }}
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
<template #actions>
|
||||
<a-link @click="onStar(item.id)">👍</a-link>
|
||||
|
@ -67,7 +69,7 @@
|
|||
<t-input v-model="formData.title"></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="固件描述" name="desc" label-align="top">
|
||||
<t-textarea :autosize="{ minRows: 5, maxRows: 10 }" v-model="formData.desc" placeholder="请输入" clearable />
|
||||
<t-textarea :autosize="{ minRows: 5, maxRows: 10 }" v-model="formData.desc" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="固件文件" name="firmware" label-align="top">
|
||||
<t-upload
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<a-col :span="4" v-for="i in state.nowpage">
|
||||
<t-card :style="{ width: '100%', marginBottom: '10px' }">
|
||||
<template #cover>
|
||||
<img style="height: 6.75vw;" :title="i.title" :src="'https://k5.vicicode.com/wsapi/download?id=' + i.id + '&n=' + i.title + '.jpg'">
|
||||
<img style="height: 6.75vw;" :title="i.title + ' [' + i.upload + ']'" :src="'https://k5.vicicode.com/wsapi/download?id=' + i.id + '&n=' + i.title + '.jpg'">
|
||||
</template>
|
||||
<template #footer>
|
||||
<t-row :align="'middle'" justify="center" style="gap: 24px">
|
||||
|
@ -77,7 +77,7 @@
|
|||
<t-input v-model="formData.title"></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="图片描述" name="desc" label-align="top">
|
||||
<t-textarea :autosize="{ minRows: 5, maxRows: 10 }" v-model="formData.desc" placeholder="请输入" clearable />
|
||||
<t-textarea :autosize="{ minRows: 5, maxRows: 10 }" v-model="formData.desc" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="图片文件" name="firmware" label-align="top">
|
||||
<t-upload
|
||||
|
|
164
src/views/idea/losehu/index.vue
Normal file
164
src/views/idea/losehu/index.vue
Normal file
|
@ -0,0 +1,164 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="[$t('menu.workshop'), $t('idea.diy')]" />
|
||||
<a-row :gutter="20" align="stretch">
|
||||
<a-col :span="24">
|
||||
<a-card class="general-card" :title="$t('idea.diy')" :loading="loading">
|
||||
<t-space direction="vertical">
|
||||
<div>操作说明:<t-link theme="primary" href="https://github.com/losehu/uv-k5-firmware-custom" target="_blank">https://github.com/losehu/uv-k5-firmware-custom</t-link></div>
|
||||
<a-radio-group v-for="item in state.showSort" v-model="state.flag[item]" type="button">
|
||||
<a-radio v-for="subItem in state.disMatrix[item]" :value="subItem[0]"
|
||||
:disabled="subItem[1]">{{ state.disName[item].get(subItem[0]) }}</a-radio>
|
||||
</a-radio-group>
|
||||
<a-button type="primary" @click="useFirmware">{{ $t('diy.generate') }}</a-button>
|
||||
</t-space>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useI18n();
|
||||
|
||||
const useFirmware = () => {
|
||||
router.push({
|
||||
path: '/tool/flash',
|
||||
query: {
|
||||
url: 'https://k5.vicicode.com/diyapi/LOSEHU' + state.flag.join('') + '.bin?v=' + (new Date()).getTime()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const state: {
|
||||
versions: any,
|
||||
flag: any,
|
||||
disMatrix: any,
|
||||
disName: any,
|
||||
showSort: any
|
||||
} = reactive({
|
||||
versions: [],
|
||||
flag: [],
|
||||
disMatrix: [],
|
||||
disName: [],
|
||||
showSort: [],
|
||||
})
|
||||
|
||||
watch(() => [...state.flag], () => { updateMatrix() })
|
||||
|
||||
const updateMatrix = () => {
|
||||
state.flag.map((e: any, i: any) => {
|
||||
state.disMatrix[i].forEach((value: any, key: any) => {
|
||||
if(state.versions.indexOf('LOSEHU' + state.flag.join('').substring(0, i) + key + state.flag.join('').substring(i+1) + '.bin') == -1){
|
||||
state.disMatrix[i].set(key, true)
|
||||
}else{
|
||||
state.disMatrix[i].set(key, false)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
|
||||
onMounted(async () => {
|
||||
setLoading(true)
|
||||
let functions = await (await fetch('https://k5.vicicode.com/diyapi/function.json?v=' + (new Date()).getTime())).text()
|
||||
functions = JSON.parse(functions)
|
||||
let _newfunc: any = []
|
||||
let _showSort: any = []
|
||||
functions.map((e: any) => {
|
||||
_newfunc[e[e.length - 1] - 1] = e
|
||||
_showSort.push(e[e.length - 1] - 1)
|
||||
})
|
||||
functions = _newfunc
|
||||
let disMatrix: any = []
|
||||
let disName: any = []
|
||||
functions.map((e: any) => {
|
||||
let _conf: any = new Map()
|
||||
let _confName: any = new Map();
|
||||
for (let i = e[0] * 2 + 1; i < e[0] * 3 + 1; i++) {
|
||||
_conf.set(e[i], false)
|
||||
if(t('idea.diy') !== 'LOSEHU DIY'){
|
||||
_confName.set(e[i], e[i - e[0] - e[0]])
|
||||
}else{
|
||||
_confName.set(e[i], e[i - e[0]])
|
||||
}
|
||||
}
|
||||
disMatrix.push(_conf)
|
||||
disName.push(_confName)
|
||||
})
|
||||
state.flag = new Array(functions.length).fill('0')
|
||||
state.disName = disName
|
||||
state.disMatrix = disMatrix
|
||||
state.showSort = _showSort
|
||||
const versions = await (await fetch('https://k5.vicicode.com/diyapi/version.json?v=' + (new Date()).getTime())).text()
|
||||
state.versions = JSON.parse(versions)
|
||||
updateMatrix()
|
||||
setLoading(false)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DIY',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
padding: 0 20px 20px 20px;
|
||||
|
||||
:deep(.arco-list-content) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:deep(.arco-card-meta-title) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-list-col) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
:deep(.arco-list-item) {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
:deep(.block-title) {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.list-wrap) {
|
||||
|
||||
// min-height: 140px;
|
||||
.list-row {
|
||||
align-items: stretch;
|
||||
|
||||
.list-col {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-space) {
|
||||
width: 100%;
|
||||
|
||||
.arco-space-item {
|
||||
&:last-child {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -105,7 +105,7 @@ const checkEeprom = async () => {
|
|||
const clearEEPROM = async () => {
|
||||
if(appStore.connectState != true){alert(sessionStorage.getItem('noticeConnectK5')); return;};
|
||||
const eepromSize = await check_eeprom(appStore.connectPort, appStore.configuration?.uart);
|
||||
let rawEEPROM = new Uint8Array(0x80);
|
||||
let rawEEPROM = new Uint8Array(0x80).fill(0xff);
|
||||
for (let i = 0; i < eepromSize; i += 0x80) {
|
||||
await eeprom_write(appStore.connectPort, i, rawEEPROM, 0x80, appStore.configuration?.uart);
|
||||
state.status = state.status + "清空进度:" + (((i - 0) / eepromSize) * 100).toFixed(1) + "%<br/>";
|
||||
|
|
|
@ -8,11 +8,20 @@
|
|||
<span @click="()=>{state.showHide += 1}">{{ $t('menu.font') + $t('global.onStart') }}</span>
|
||||
</template>
|
||||
<a-space>
|
||||
<t-card bordered>
|
||||
<t-card bordered style="width: 420px;">
|
||||
<template #header>
|
||||
{{ $t('tool.fontwrite') }}
|
||||
<div>
|
||||
<a-radio-group type="button" size="mini" v-model="state.lang">
|
||||
<a-radio value="Simplified_Chinese">{{$t('tool.Simplified_Chinese')}}</a-radio>
|
||||
<a-radio value="Traditional_Chinese">{{$t('tool.Traditional_Chinese')}}</a-radio>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
<a-button @click="restore(1)">{{ $t('tool.writefontwrite') }}</a-button>
|
||||
<div>
|
||||
<a-button v-show="state.lang == 'Simplified_Chinese'" @click="restore(1)">{{ $t('tool.writefontwrite') }}</a-button>
|
||||
<a-button v-show="state.lang == 'Traditional_Chinese'" @click="restore(6)">{{ $t('tool.writefontwrite') }}</a-button>
|
||||
</div>
|
||||
</t-card>
|
||||
<t-card bordered>
|
||||
<template #header>
|
||||
|
@ -49,7 +58,8 @@ const appStore = useAppStore();
|
|||
const state = reactive({
|
||||
status: "点击写入按钮写入字库到设备<br/><br/>",
|
||||
eepromType: "",
|
||||
showHide: 0
|
||||
showHide: 0,
|
||||
lang: 'Simplified_Chinese'
|
||||
})
|
||||
|
||||
const restoreRange = async (start: any = 0, uint8Array: any) => {
|
||||
|
@ -145,6 +155,25 @@ const restore = async(type: any = 1) => {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if(type == 6){
|
||||
if(appStore.configuration?.charset == "gb2312"){
|
||||
fontPacket = await fetch('/new_font_k_f.bin')
|
||||
const reader = fontPacket.body.getReader();
|
||||
const chunks = [];
|
||||
while(true) {
|
||||
const {done, value} = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
chunks.push(...value)
|
||||
}
|
||||
const binary = new Uint8Array(chunks)
|
||||
await restoreRange(0x02480, binary)
|
||||
return;
|
||||
}else{
|
||||
alert('不支持的版本')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ onMounted(async ()=>{
|
|||
}
|
||||
const binary = new Uint8Array(chunks)
|
||||
state.binaryFile = binary
|
||||
state.binaryName = route.query.url.substring(route.query.url.lastIndexOf('/') + 1)
|
||||
state.binaryName = route.query.url.substring(route.query.url.lastIndexOf('/') + 1).split('?')[0] + ' '
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
272
src/views/list/mdc/index.vue
Normal file
272
src/views/list/mdc/index.vue
Normal file
|
@ -0,0 +1,272 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="[$t('menu.dashboard'), $t('menu.cps.mdc')]" />
|
||||
<a-card class="general-card">
|
||||
<template #title>
|
||||
<span @click="()=>{istate.showHide += 1}">{{ $t('menu.cps.mdc') + $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-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, watch } from 'vue';
|
||||
import { Input } from 'tdesign-vue-next';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { eeprom_read, uint8ArrayToHexReverseString, hexReverseStringToUint8Array, stringToUint8Array, uint8ArrayToString, 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: 16}).map(e=>{return {}}),
|
||||
pageSize: 16,
|
||||
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.contact'),
|
||||
colKey: 'name',
|
||||
width: 250,
|
||||
align: 'left',
|
||||
cell: (h, { row }) => row.name ? row.name.replace(/[^a-zA-Z0-9_]/g, '') : undefined,
|
||||
edit: {
|
||||
component: Input,
|
||||
props: {
|
||||
clearable: true
|
||||
},
|
||||
onEdited: (context: any) => {
|
||||
const newData = [...cstate.renderData];
|
||||
newData.splice((cstate.nowPage - 1) * cstate.pageSize + context.rowIndex, 1, context.newRowData);
|
||||
cstate.renderData = newData;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('cps.mdcid'),
|
||||
colKey: 'mdcid',
|
||||
align: 'left',
|
||||
width: 200,
|
||||
cell: (h, { row }) => parseInt(row.mdcid, 16) <= 65535 ? parseInt(row.mdcid, 16).toString(16) : undefined,
|
||||
edit: {
|
||||
component: Input,
|
||||
props: {
|
||||
clearable: true
|
||||
},
|
||||
onEdited: (context: any) => {
|
||||
context.newRowData.mdcid = context.newRowData.mdcid ? context.newRowData.mdcid.toLowerCase() : 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(0x100);
|
||||
for (let i = 0x1D00; i < 0x1E00; i += 0x10) {
|
||||
const _data = await eeprom_read(appStore.connectPort, i, 0x10, appStore.configuration?.uart)
|
||||
rawEEPROM.set(_data, i - 0x1D00)
|
||||
}
|
||||
const _renderData : any = [];
|
||||
for (let i = 0; i < 0x100; i += 0x10) {
|
||||
if(uint8ArrayToHexReverseString(rawEEPROM.subarray(i, i + 0x02)) != 'ffff'){
|
||||
_renderData.push({
|
||||
name: uint8ArrayToString(rawEEPROM.subarray(i + 0x02, i + 0x10), appStore.configuration?.charset).trim(),
|
||||
mdcid: uint8ArrayToHexReverseString(rawEEPROM.subarray(i, i + 0x01)) + uint8ArrayToHexReverseString(rawEEPROM.subarray(i + 0x01, i + 0x02))
|
||||
})
|
||||
}else{
|
||||
_renderData.push({})
|
||||
}
|
||||
}
|
||||
cstate.renderData = _renderData;
|
||||
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 < 0x100; i += 0x10) {
|
||||
if(cstate.renderData[i / 0x10].mdcid){
|
||||
const _data = new Uint8Array(0x10).fill(0x20)
|
||||
_data.set(hexReverseStringToUint8Array(cstate.renderData[i / 0x10].mdcid.padStart(4, '0').substring(0, 2)))
|
||||
_data.set(hexReverseStringToUint8Array(cstate.renderData[i / 0x10].mdcid.padStart(4, '0').substring(2, 4)), 0x01)
|
||||
_data.set(stringToUint8Array(cstate.renderData[i / 0x10].name), 0x02)
|
||||
await eeprom_write(
|
||||
appStore.connectPort,
|
||||
i + 0x1D00,
|
||||
_data,
|
||||
0x10,
|
||||
appStore.configuration?.uart
|
||||
);
|
||||
}else{
|
||||
await eeprom_write(
|
||||
appStore.connectPort,
|
||||
i + 0x1D00,
|
||||
hexReverseStringToUint8Array('ffffffffffffffffffffffffffffffff'),
|
||||
0x10,
|
||||
appStore.configuration?.uart
|
||||
);
|
||||
}
|
||||
}
|
||||
const _tmp = await eeprom_read(appStore.connectPort, 0x1FF0, 0x10, appStore.configuration?.uart)
|
||||
_tmp.set([0x10], 0x10 - 1)
|
||||
await eeprom_write(
|
||||
appStore.connectPort,
|
||||
0x1FF0,
|
||||
_tmp,
|
||||
0x10,
|
||||
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>
|
||||
|
272
src/views/list/radio/index.vue
Normal file
272
src/views/list/radio/index.vue
Normal file
|
@ -0,0 +1,272 @@
|
|||
<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, watch } 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 {}}),
|
||||
pageSize: 50,
|
||||
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)
|
||||
if(appStore.configuration?.fm30){
|
||||
let rawEEPROM = new Uint8Array(0x03C);
|
||||
for (let i = 0x1FFC0; i < 0x1FFF1; i += 0x08) {
|
||||
const _data = await eeprom_read(appStore.connectPort, i, 0x08, appStore.configuration?.uart)
|
||||
rawEEPROM.set(_data, i - 0x1FFC0)
|
||||
}
|
||||
const _renderData : any = [];
|
||||
for (let i = 0; i < 0x03C; 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;
|
||||
}else{
|
||||
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;
|
||||
}
|
||||
setLoading(false)
|
||||
}
|
||||
const writeChannel = async() =>{
|
||||
if(appStore.connectState != true){alert(sessionStorage.getItem('noticeConnectK5')); return;};
|
||||
await eeprom_init(appStore.connectPort);
|
||||
setLoading(true)
|
||||
if(appStore.configuration?.fm30){
|
||||
for (let i = 0; i < 0x03C; i += 0x02) {
|
||||
if(cstate.renderData[i / 0x02].rx){
|
||||
await eeprom_write(appStore.connectPort, i + 0x1FFC0, hexReverseStringToUint8Array((parseInt(cstate.renderData[i / 0x02].rx * 10)).toString(16).padStart(4, '0')), 0x02, appStore.configuration?.uart);
|
||||
}else{
|
||||
await eeprom_write(appStore.connectPort, i + 0x1FFC0, hexReverseStringToUint8Array('0000'), 0x02, appStore.configuration?.uart);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
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>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<a-modal v-model:visible="state.visible" @ok="handleOk" ok-text="Scanned and uploaded">
|
||||
<a-modal v-model:visible="state.visible" @ok="handleOk" :ok-text="$t('tool.scaned')">
|
||||
<template #title>
|
||||
{{ $t('tool.scanqr') }}
|
||||
</template>
|
||||
|
@ -47,7 +47,7 @@
|
|||
<a-space>
|
||||
<a-button @click="getLocation">{{ $t('tool.brlonlat') }}</a-button>
|
||||
<a-button @click="scanLocation">{{ $t('tool.phonelonlat') }}</a-button>
|
||||
<a-button @click="getPass">{{ $t('tool.satpasstime') }}</a-button>
|
||||
<a-button type="primary" @click="getPass">{{ $t('tool.satpasstime') }}</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-form-item :label-col-style="{ width: '25%' }" field="pass" :label="$t('tool.selectPassTime')">
|
||||
|
@ -84,7 +84,7 @@
|
|||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label-col-style="{ width: '25%' }" label="">
|
||||
<a-button @click="writeIt">{{ $t('tool.writeData') }}</a-button>
|
||||
<a-button type="primary" @click="writeIt">{{ $t('tool.writeData') }}</a-button>
|
||||
</a-form-item>
|
||||
<a-divider />
|
||||
<div id="statusArea"
|
||||
|
|
|
@ -102,7 +102,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, reactive } from 'vue';
|
||||
import { ref, computed, reactive, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { Input, Select } from 'tdesign-vue-next';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { eeprom_read, uint8ArrayToHexReverseString, uint8ArrayToString, hexReverseStringToUint8Array, stringToUint8Array, eeprom_write, eeprom_reboot, eeprom_init } from '@/utils/serial.js';
|
||||
|
@ -161,6 +162,93 @@
|
|||
cstate.renderData = params.newData
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
onMounted(async ()=>{
|
||||
if(route.query.url){
|
||||
const packet = await fetch(route.query.url)
|
||||
const reader = packet?.body?.getReader();
|
||||
if(reader){
|
||||
const chunks = [];
|
||||
while(true) {
|
||||
const {done, value} = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
chunks.push(...value)
|
||||
}
|
||||
const binary = new Uint8Array(chunks)
|
||||
var workbook = xlsxRead(binary);
|
||||
const renderData : any = Array.from({length: 200}).map(e=>{return {scanlist: []}})
|
||||
for(let i = 2; i < 202; i++){
|
||||
if(workbook.Sheets.Sheet1['B' + i]?.w){
|
||||
renderData[i - 2]['name'] = workbook.Sheets.Sheet1['B' + i]?.w
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['C' + i]?.w){
|
||||
renderData[i - 2]['bandwidth'] = Object.keys(state.bandwidthOption).find(key=>state.bandwidthOption[key]==workbook.Sheets.Sheet1['C' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['D' + i]?.w){
|
||||
renderData[i - 2]['rx'] = workbook.Sheets.Sheet1['D' + i]?.w
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['E' + i]?.w){
|
||||
renderData[i - 2]['tx'] = workbook.Sheets.Sheet1['E' + i]?.w
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['F' + i]?.w){
|
||||
renderData[i - 2]['power'] = Object.keys(state.powerOption).find(key=>state.powerOption[key]==workbook.Sheets.Sheet1['F' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['G' + i]?.w){
|
||||
renderData[i - 2]['rxTone'] = Object.keys(state.toneOption).find(key=>state.toneOption[key]==workbook.Sheets.Sheet1['G' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['H' + i]?.w){
|
||||
renderData[i - 2]['rxCTCSS'] = parseFloat(workbook.Sheets.Sheet1['H' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['I' + i]?.w){
|
||||
renderData[i - 2]['rxDCS'] = parseFloat(workbook.Sheets.Sheet1['I' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['J' + i]?.w){
|
||||
renderData[i - 2]['txTone'] = Object.keys(state.toneOption).find(key=>state.toneOption[key]==workbook.Sheets.Sheet1['J' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['K' + i]?.w){
|
||||
renderData[i - 2]['txCTCSS'] = parseFloat(workbook.Sheets.Sheet1['K' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['L' + i]?.w){
|
||||
renderData[i - 2]['txDCS'] = parseFloat(workbook.Sheets.Sheet1['L' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['M' + i]?.w){
|
||||
renderData[i - 2]['step'] = parseFloat(workbook.Sheets.Sheet1['M' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['N' + i]?.w){
|
||||
renderData[i - 2]['reverse'] = workbook.Sheets.Sheet1['N' + i]?.w == '开' ? true : false
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['O' + i]?.w){
|
||||
renderData[i - 2]['scramb'] = parseFloat(workbook.Sheets.Sheet1['O' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['P' + i]?.w){
|
||||
renderData[i - 2]['busy'] = workbook.Sheets.Sheet1['P' + i]?.w == '开' ? true : false
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['Q' + i]?.w){
|
||||
renderData[i - 2]['pttid'] = workbook.Sheets.Sheet1['Q' + i]?.w
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['R' + i]?.w){
|
||||
renderData[i - 2]['mode'] = Object.keys(state.modeOption).find(key=>state.modeOption[key]==workbook.Sheets.Sheet1['R' + i]?.w)
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['S' + i]?.w){
|
||||
renderData[i - 2]['dtmf'] = workbook.Sheets.Sheet1['S' + i]?.w == '开' ? true : false
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['T' + i]?.w){
|
||||
if(workbook.Sheets.Sheet1['T' + i]?.w.split(',').indexOf('I') >= 0){
|
||||
renderData[i - 2]['scanlist'].push('I')
|
||||
}
|
||||
if(workbook.Sheets.Sheet1['T' + i]?.w.split(',').indexOf('II') >= 0){
|
||||
renderData[i - 2]['scanlist'].push('II')
|
||||
}
|
||||
}
|
||||
}
|
||||
cstate.renderData = renderData
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
colKey: 'drag', // 列拖拽排序必要参数
|
||||
|
@ -626,7 +714,11 @@
|
|||
const _tx = _channel.tx && _channel.rx ? Math.abs(parseInt((_channel.tx * 100000).toFixed(0)) - parseInt((_channel.rx * 100000).toFixed(0))) : NaN
|
||||
_channelhex += !Number.isNaN(_tx) ? _tx.toString(16).padStart(8, '0') : '00000000'
|
||||
_channelhex += parseInt((_channel.rx * 100000).toFixed(0)).toString(16).padStart(8, '0')
|
||||
|
||||
|
||||
if(_channelhex.indexOf('-1') != -1){
|
||||
_channelhex = _channelhex.replace(/^(.{10})(.{6})(.*)$/, '$1000000$3');
|
||||
}
|
||||
|
||||
console.log(_channelhex)
|
||||
rawEEPROM.set(hexReverseStringToUint8Array(_channelhex), i)
|
||||
|
||||
|
@ -774,6 +866,7 @@
|
|||
}
|
||||
xlsxWrite(workbook, 'K5Channel.xlsx');
|
||||
}
|
||||
|
||||
const restoreExcelChannel = () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
|
|
|
@ -21,16 +21,6 @@
|
|||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
<!-- <a-col :span="12" style="text-align: right;">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="saveChannel">
|
||||
{{ $t('cps.save') }}
|
||||
</a-button>
|
||||
<a-button @click="restoreChannel">
|
||||
{{ $t('cps.load') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col> -->
|
||||
</a-row>
|
||||
<a-spin :loading="loading" style="width: 100%;">
|
||||
<a-form-item :label-col-style="{width: '25%'}" field="logo_line1" :label="$t('cps.line1')">
|
||||
|
@ -39,7 +29,10 @@
|
|||
<a-form-item :label-col-style="{width: '25%'}" field="logo_line2" :label="$t('cps.line2')">
|
||||
<a-input v-model="state.logo_line2" />
|
||||
</a-form-item>
|
||||
<a-form-item :label-col-style="{width: '25%'}" field="logo_line2" :label="$t('cps.mdclocplay')">
|
||||
<a-form-item :label-col-style="{width: '25%'}" field="dtmfid" :label="$t('cps.dtmfid')">
|
||||
<a-input v-model="state.dtmfid" />
|
||||
</a-form-item>
|
||||
<a-form-item :label-col-style="{width: '25%'}" field="mdclocplay" :label="$t('cps.mdclocplay')">
|
||||
<a-switch v-model="state.mdc_audio_local" type="round"/>
|
||||
</a-form-item>
|
||||
</a-spin>
|
||||
|
@ -61,7 +54,8 @@ const { loading, setLoading } = useLoading(false);
|
|||
const state = reactive({
|
||||
logo_line1: '',
|
||||
logo_line2: '',
|
||||
mdc_audio_local: true
|
||||
mdc_audio_local: true,
|
||||
dtmfid: ''
|
||||
})
|
||||
|
||||
const readChannel = async() => {
|
||||
|
@ -85,6 +79,9 @@ const readChannel = async() => {
|
|||
state.logo_line2 = uint8ArrayToString(logo.subarray(0x10, 0x20), appStore.configuration?.charset)
|
||||
}
|
||||
|
||||
const dtmfid = await eeprom_read(appStore.connectPort, 0xEE0, 0x03, appStore.configuration?.uart)
|
||||
state.dtmfid = uint8ArrayToString(dtmfid)
|
||||
|
||||
if(parseInt(await eeprom_read(appStore.connectPort, 0x01FFD, 0x01, appStore.configuration?.uart)) == 0){
|
||||
state.mdc_audio_local = false
|
||||
}else{
|
||||
|
@ -116,21 +113,21 @@ const writeChannel = async() => {
|
|||
logo.set(stringToUint8Array(state.logo_line2, appStore.configuration?.charset).subarray(0, 0x10), 0x10);
|
||||
await eeprom_write(appStore.connectPort, 0xEB0, logo, 0x20, appStore.configuration?.uart);
|
||||
}
|
||||
if(state.dtmfid == ''){
|
||||
await eeprom_write(appStore.connectPort, 0xEE0, new Uint8Array([0xff, 0xff, 0xff]), 0x03, appStore.configuration?.uart);
|
||||
}else{
|
||||
await eeprom_write(appStore.connectPort, 0xEE0, new Uint8Array([
|
||||
stringToUint8Array(state.dtmfid.padStart(3, '0').split('')[0]),
|
||||
stringToUint8Array(state.dtmfid.padStart(3, '0').split('')[1]),
|
||||
stringToUint8Array(state.dtmfid.padStart(3, '0').split('')[2])
|
||||
]), 0x03, appStore.configuration?.uart);
|
||||
}
|
||||
if(appStore.configuration?.localmdc){
|
||||
await eeprom_write(appStore.connectPort, 0x01FFD, new Uint8Array([state.mdc_audio_local ? 1 : 0]), 0x01, appStore.configuration?.uart);
|
||||
}
|
||||
await eeprom_reboot(appStore.connectPort);
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
const saveChannel = async() => {
|
||||
|
||||
}
|
||||
|
||||
const restoreChannel = async() => {
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
|
|
@ -7530,6 +7530,11 @@ vue-i18n@^9.2.2:
|
|||
"@intlify/shared" "9.11.1"
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
|
||||
vue-matomo@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://repo.vicicode.com/repository/npm-proxy/vue-matomo/-/vue-matomo-4.2.0.tgz#d65e369e4ead1d95ef790bef3627512cac3d25e9"
|
||||
integrity sha512-m5hCw7LH3wPDcERaF4sp/ojR9sEx7Rl8TpOyH/4jjQxMF2DuY/q5pO+i9o5Dx+BXLSa9+IQ0qhAbWYRyESQXmA==
|
||||
|
||||
vue-router@^4.0.14:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.3.0.tgz"
|
||||
|
|
Loading…
Reference in a new issue