| | |
| | | } |
| | | |
| | | export const uKeyLogin = (username, password, uKeyId) => { |
| | | console.log(username + password + uKeyId); |
| | | return axios({ |
| | | method: "post", |
| | | url: `LoginAction_login4UKey?uinf.UName=${username}&uinf.Upassword=${md5(password)}&uinf.UId=0&uinf.UKey_ID=${uKeyId}`, |
| | |
| | | method: "post", |
| | | url: "Battalarm_dataAction!getRealAlarm", |
| | | }); |
| | | } |
| | | |
| | | export const getRandomFromServer = ()=> { |
| | | return axios({ |
| | | method: 'post', |
| | | url: 'LoginAction_getRandom' |
| | | }); |
| | | } |
| | | |
| | | export const loginSignVerity = (data)=> { |
| | | return axios({ |
| | | method: 'post', |
| | | url: 'LoginAction_verify', |
| | | data: 'json='+JSON.stringify(data), |
| | | }); |
| | | } |
| | |
| | | }) |
| | | }); |
| | | }, |
| | | bindUKey(UName, UKeyId) { |
| | | bindUKey(UName, UKeyId, UPubKeyX, UPubKeyY) { |
| | | return axios({ |
| | | method: "post", |
| | | url: 'User_infAction!bindUKey', |
| | | data: 'json='+JSON.stringify({ |
| | | UName: UName, |
| | | UKey_ID:UKeyId |
| | | UKey_ID:UKeyId, |
| | | UPubKeyX, |
| | | UPubKeyY |
| | | }) |
| | | }); |
| | | } |
| | |
| | | show: false |
| | | }, |
| | | { |
| | | id: 5, |
| | | name: "stopReason", |
| | | type: "", |
| | | icon: "", |
| | | text: "核容终止原因:", |
| | | value: "未知", |
| | | show: false |
| | | }, |
| | | { |
| | | id: 6, |
| | | name: "failReason", |
| | | type: "", |
| | | icon: "", |
| | | text: "操作失败原因:", |
| | | value: "未知", |
| | | show: false |
| | | }, |
| | | { |
| | | id: 7, |
| | | name: "resDay", |
| | | type: "", |
| | |
| | | show: false |
| | | } |
| | | ], |
| | | historyStateList: [ |
| | | { |
| | | id: 5, |
| | | name: "stopReason", |
| | | type: "", |
| | | icon: "", |
| | | text: "核容终止原因:", |
| | | value: "未知", |
| | | show: false |
| | | }, |
| | | { |
| | | id: 6, |
| | | name: "failReason", |
| | | type: "", |
| | | icon: "", |
| | | text: "操作失败原因:", |
| | | value: "未知", |
| | | show: false |
| | | }, |
| | | ], |
| | | getItemByName(name, list) { |
| | | let result = false; |
| | | for(let i=0; i<list.length; i++) { |
New file |
| | |
| | | function SoftUKey(SoftKey3W) { |
| | | this.SoftKey3W = SoftKey3W; |
| | | } |
| | | |
| | | /** |
| | | * uKey写入用户名 |
| | | * @param userName 用户名 |
| | | * @param callback 回调函数 |
| | | */ |
| | | SoftUKey.prototype.setUserName = function (userName, callback) { |
| | | try { |
| | | let s_simnew1 = new this.SoftKey3W(); //创建UK类 |
| | | let DevicePath, PriKey, PubKeyX, PubKeyY; |
| | | // 开启监听 |
| | | s_simnew1.Socket_UK.onopen = function() { |
| | | s_simnew1.ResetOrder();//这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程 |
| | | } |
| | | |
| | | // 监听获取到的信息 |
| | | s_simnew1.Socket_UK.onmessage = function(Msg) { |
| | | let UK_Data = JSON.parse(Msg.data); |
| | | if(UK_Data.type!="Process")return ;//如果不是流程处理消息,则跳过 |
| | | |
| | | // 根据收到的序号执行函数 |
| | | switch(UK_Data.order) { |
| | | case 0: |
| | | s_simnew1.FindPort(0); //发送命令取UK的路径 |
| | | break; |
| | | case 1: |
| | | if(UK_Data.LastError!=0) { |
| | | window.alert ( "未发现加密锁,请插入加密锁"); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | // 设置路径 |
| | | DevicePath=UK_Data.return_value;//获得返回的UK的路径 |
| | | |
| | | //产生密钥对 |
| | | s_simnew1.StarGenKeyPair(DevicePath); |
| | | break; |
| | | case 2: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("产生密钥对出现错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | |
| | | //获取生成的私钥 |
| | | s_simnew1.GenPriKey(); |
| | | break; |
| | | case 3: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("获取生成的私钥时错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | PriKey = UK_Data.return_value; |
| | | //获取生成的公钥X |
| | | s_simnew1.GenPubKeyX(); |
| | | break; |
| | | case 4: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("获取生成的公钥X时错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | PubKeyX = UK_Data.return_value; |
| | | |
| | | //获取生成的公钥Y |
| | | s_simnew1.GenPubKeyY(); |
| | | break; |
| | | case 5: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("获取生成的公钥Y时错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | PubKeyY = UK_Data.return_value; |
| | | |
| | | //设置密钥对及用户身份到锁中 |
| | | s_simnew1.Set_SM2_KeyPair(PriKey,PubKeyX,PubKeyY,userName,DevicePath); |
| | | break; |
| | | case 6: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("设置密钥对及用户身份时出现错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | // 执行回调函数 |
| | | if(typeof callback == 'function') { |
| | | callback({ |
| | | PriKey, |
| | | PubKeyX, |
| | | PubKeyY, |
| | | userName, |
| | | DevicePath |
| | | }); |
| | | } |
| | | |
| | | //所有工作处理完成后,关掉Socket |
| | | s_simnew1.Socket_UK.close(); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // 关闭监听 |
| | | s_simnew1.Socket_UK.onclose = function() { |
| | | console.log("关闭设置用户名称") |
| | | } |
| | | return true; |
| | | }catch(e) { |
| | | console.log("设置用户名称异常:"+e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 读取uKey中的用户名 |
| | | * @param callback |
| | | * @return {boolean} |
| | | */ |
| | | SoftUKey.prototype.getUserName = function(callback) { |
| | | try { |
| | | let s_simnew1 = new this.SoftKey3W(); //创建UK类 |
| | | let DevicePath, userName; |
| | | // 开启监听 |
| | | s_simnew1.Socket_UK.onopen = function() { |
| | | s_simnew1.ResetOrder();//这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程 |
| | | } |
| | | |
| | | // 监听获取到的信息 |
| | | s_simnew1.Socket_UK.onmessage = function(Msg) { |
| | | let UK_Data = JSON.parse(Msg.data); |
| | | if(UK_Data.type!="Process")return ;//如果不是流程处理消息,则跳过 |
| | | |
| | | // 根据收到的序号执行函数 |
| | | switch(UK_Data.order) { |
| | | case 0: |
| | | s_simnew1.FindPort(0); //发送命令取UK的路径 |
| | | break; |
| | | case 1: |
| | | if(UK_Data.LastError!=0) { |
| | | window.alert ( "未发现加密锁,请插入加密锁"); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | // 设置路径 |
| | | DevicePath=UK_Data.return_value;//获得返回的UK的路径 |
| | | |
| | | //返回设置在锁中的身份 |
| | | s_simnew1.GetSm2UserName(DevicePath); |
| | | break; |
| | | case 2: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("返回设置在锁中的用户身份时出现错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | userName = UK_Data.return_value; |
| | | |
| | | // 执行回调函数 |
| | | if(typeof callback == 'function') { |
| | | callback({ |
| | | userName, |
| | | }); |
| | | } |
| | | |
| | | //所有工作处理完成后,关掉Socket |
| | | s_simnew1.Socket_UK.close(); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // 关闭监听 |
| | | s_simnew1.Socket_UK.onclose = function() { |
| | | console.log("关闭读取用户名称") |
| | | } |
| | | return true; |
| | | }catch (e) { |
| | | console.log("读取用户名称异常:"+e); |
| | | } |
| | | } |
| | | /** |
| | | * 获取ukey的签名 |
| | | * @param randomNumber |
| | | * @param callback |
| | | */ |
| | | SoftUKey.prototype.getSign = function(randomNumber, callback) { |
| | | try { |
| | | let s_simnew1 = new this.SoftKey3W(); //创建UK类 |
| | | let inPath, sign, uKeyId, publicX, publicY; |
| | | // 开启监听 |
| | | s_simnew1.Socket_UK.onopen = function() { |
| | | s_simnew1.ResetOrder();//这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程 |
| | | } |
| | | |
| | | // 监听获取到的信息 |
| | | s_simnew1.Socket_UK.onmessage = function(Msg) { |
| | | let UK_Data = JSON.parse(Msg.data); |
| | | if(UK_Data.type!="Process")return ;//如果不是流程处理消息,则跳过 |
| | | |
| | | // 根据收到的序号执行函数 |
| | | switch(UK_Data.order) { |
| | | case 0: |
| | | s_simnew1.FindPort(0); //发送命令取UK的路径 |
| | | break; |
| | | case 1: |
| | | if(UK_Data.LastError!=0) { |
| | | window.alert ( "未发现加密锁,请插入加密锁"); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | // 设置路径 |
| | | inPath=UK_Data.return_value;//获得返回的UK的路径 |
| | | |
| | | s_simnew1.GetProduceDate(inPath);//返回芯片唯一ID |
| | | break; |
| | | case 2: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("返回芯片唯一ID时出现错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | uKeyId = UK_Data.return_value; |
| | | |
| | | //返回设置在锁中的公钥X |
| | | s_simnew1.GetPubKeyX(inPath); |
| | | break; |
| | | case 3: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("返回设置在锁中的公钥X时出现错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | publicX = UK_Data.return_value; |
| | | |
| | | // 返回设置在锁中的公钥Y |
| | | s_simnew1.GetPubKeyY(inPath); |
| | | break; |
| | | case 4: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("返回设置在锁中的公钥Y时出现错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | publicY = UK_Data.return_value; |
| | | |
| | | // 对数据进行签名 |
| | | s_simnew1.YtSign(randomNumber,"123", inPath); |
| | | break; |
| | | case 5: |
| | | if(UK_Data.LastError!=0){ |
| | | window.alert("对数据进行签名时出现错误,错误码为:"+UK_Data.LastError.toString()); |
| | | s_simnew1.Socket_UK.close(); |
| | | return false; |
| | | } |
| | | sign = UK_Data.return_value; |
| | | |
| | | // 执行回调函数 |
| | | if(typeof callback == 'function') { |
| | | callback({ |
| | | inPath, |
| | | sign, |
| | | uKeyId, |
| | | publicX, |
| | | publicY, |
| | | }); |
| | | } |
| | | |
| | | //所有工作处理完成后,关掉Socket |
| | | s_simnew1.Socket_UK.close(); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // 关闭监听 |
| | | s_simnew1.Socket_UK.onclose = function() { |
| | | console.log("关闭获取签名") |
| | | } |
| | | }catch (e) { |
| | | console.log("获取签名异常:"+e) |
| | | } |
| | | } |
| | | |
| | | export default SoftUKey; |
| | |
| | | checkUKey(id) { |
| | | if(id) { |
| | | let loading = this.$layer.loading(); |
| | | this.$apis.uKey.getUNameByUKey(id).then(res=>{ |
| | | this.$apis.uKey.getUNameByUKey(id.trim()).then(res=>{ |
| | | let rs = JSON.parse(res.data.result); |
| | | console.log(rs); |
| | | if(rs.code == 1) { |
| | |
| | | return msg; |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.checkUKey(this.id); |
| | | } |
| | | } |
| | | </script> |
| | | |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import SoftUKey from "@/assets/js/tools/SoftUKey"; |
| | | import SoftKey3W from "@/assets/js/Syunew3"; |
| | | export default { |
| | | name: "BindStep3", |
| | | props: { |
| | |
| | | data() { |
| | | return { |
| | | userName: "", |
| | | softUKey: new SoftUKey(SoftKey3W) |
| | | } |
| | | }, |
| | | methods: { |
| | |
| | | // 关闭等待框 |
| | | this.$layer.close(loading); |
| | | }else { |
| | | this.bindUkey(name, id, loading); |
| | | this.softUKey.setUserName(name, this.setUserNameRs); |
| | | // 关闭等待框 |
| | | this.$layer.close(loading); |
| | | |
| | | } |
| | | }).catch(error=>{ |
| | | // 关闭等待框 |
| | |
| | | console.log(error); |
| | | }); |
| | | }, |
| | | setUserNameRs(result) { |
| | | let name = result.userName; |
| | | let id = this.id.trim(); |
| | | let publicX = result.PubKeyX; |
| | | let publicY = result.PubKeyY; |
| | | let loading = this.$layer.loading(); |
| | | this.bindUkey(name, id, publicX, publicY, loading); |
| | | }, |
| | | /** |
| | | * 绑定用户 |
| | | * @param name 用户名 |
| | | * @param id ukey的信息 |
| | | * @param loading 加载等待框 |
| | | */ |
| | | bindUkey(name, id, loading) { |
| | | bindUkey(name, id, publicX, publicY, loading) { |
| | | if(!loading) { |
| | | loading = this.$layer.loading(); |
| | | } |
| | | this.$apis.uKey.bindUKey(name, id).then(res=>{ |
| | | this.$apis.uKey.bindUKey(name, id, publicX, publicY).then(res=>{ |
| | | let rs = JSON.parse(res.data.result); |
| | | if(rs.code == 1) { |
| | | this.$layer.msg('绑定成功'); |
| | |
| | | </div> |
| | | <el-tabs v-model="acTabs" type="border-card" class="flex-layout noborder" @tab-click="tabClick"> |
| | | <el-tab-pane label="电路拓扑图" name="eleLine"> |
| | | <science-box :top="8" :left="8" no-header v-show="stateListState"> |
| | | <div class="hdw-state-list table-layout"> |
| | | <div v-for="state in showStateList" :key="state.text" class="table-row" |
| | | :class="state.type" v-show="!state.notShow"> |
| | | <div class="table-cell text-right"> |
| | | <i v-if="state.icon" class="iconfont" :class="state.icon"></i>{{ |
| | | state.text |
| | | }} |
| | | </div> |
| | | <div class="table-cell"> |
| | | {{ state.value }}{{ state.unit }} |
| | | <div style="position: absolute;top: 8px; left: 8px"> |
| | | <science-box style="position: relative" no-header v-show="stateListState"> |
| | | <div class="hdw-state-list table-layout"> |
| | | <div v-for="state in showStateList" :key="state.text" class="table-row" |
| | | :class="state.type" v-show="!state.notShow"> |
| | | <div class="table-cell text-right"> |
| | | <i v-if="state.icon" class="iconfont" :class="state.icon"></i>{{ |
| | | state.text |
| | | }} |
| | | </div> |
| | | <div class="table-cell"> |
| | | {{ state.value }}{{ state.unit }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <table class="table-info-list" v-if="fodShow"> |
| | | <thead> |
| | | <table class="table-info-list" v-if="fodShow"> |
| | | <thead> |
| | | <tr> |
| | | <td v-for="item in fodHeaders" :key="item.prop">{{ item.label }}</td> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | </thead> |
| | | <tbody> |
| | | <tr v-for="(fod, key) in fodData" :key="key"> |
| | | <td v-for="(item, index) in fod" :key="index">{{ fod[index] }}</td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | </science-box> |
| | | </tbody> |
| | | </table> |
| | | </science-box> |
| | | <science-box style="position: relative; margin-top: 8px" no-header v-show="stateListState"> |
| | | <div class="hdw-state-list table-layout"> |
| | | <div v-for="state in showHistoryStateList" :key="state.text" class="table-row" |
| | | :class="state.type" v-show="!state.notShow"> |
| | | <div class="table-cell text-right"> |
| | | <i v-if="state.icon" class="iconfont" :class="state.icon"></i>{{ |
| | | state.text |
| | | }} |
| | | </div> |
| | | <div class="table-cell"> |
| | | {{ state.value }}{{ state.unit }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </science-box> |
| | | </div> |
| | | |
| | | <circuit-diagram :batt="batt" :online-vol="inputs.online_vol" :group-vol="inputs.group_vol" |
| | | :batt-curr="inputs.group_curr" :dev-temp="diagram.temp" :type="diagram.type" |
| | | :update="diagram.update" :version="dev_version" :contact-res="diagram.contactRes" |
| | |
| | | let permits = this.$store.state.user.permits; |
| | | let isCanTest = isHasPermit("batt_test_op_permit", permits); |
| | | let stateList = const_61850.stateList; |
| | | let historyStateList = const_61850.historyStateList; |
| | | return { |
| | | maskShow: false, |
| | | eleOffImg: require('../../assets/images/eleOff.png'), |
| | |
| | | batt: {}, |
| | | stateListShow: true, |
| | | stateList: stateList, |
| | | historyStateList: historyStateList, |
| | | timer: new Timeout("realTime"), |
| | | diagram: { |
| | | update: true, |
| | |
| | | state.type = type ? type : ""; |
| | | } |
| | | } |
| | | |
| | | let historyStateList = this.historyStateList; |
| | | for (let i = 0; i < historyStateList.length; i++) { |
| | | let state = historyStateList[i]; |
| | | if (state.name == name) { |
| | | state.value = value; |
| | | state.type = type ? type : ""; |
| | | } |
| | | } |
| | | }, |
| | | /* echars图表 */ |
| | | realTimeSearch() { |
| | |
| | | if (rs.code == 1) { |
| | | let data = rs.data; |
| | | this.stateList.forEach((item) => { |
| | | item.show = this.getStateById(item.id, data); |
| | | }); |
| | | |
| | | this.historyStateList.forEach((item) => { |
| | | item.show = this.getStateById(item.id, data); |
| | | }); |
| | | } |
| | |
| | | } |
| | | }); |
| | | }, |
| | | showHistoryStateList() { |
| | | return this.historyStateList.filter((item) => { |
| | | if (item.show) { |
| | | return item; |
| | | } |
| | | }); |
| | | }, |
| | | dischargeDialogTitle() { |
| | | let batt = this.batt; |
| | | if (regEquipType(batt.FBSDeviceId, "equip61850")) { |
| | |
| | | stateListState() { |
| | | return this.stateListShow && this.showStateList.length; |
| | | }, |
| | | historyStateListState() { |
| | | return this.stateListShow && this.showHistoryStateList.length; |
| | | }, |
| | | histroyDataTitle() { |
| | | return this.batt.StationName + "-历史实时数据"; |
| | | }, |
| | |
| | | import { |
| | | login, |
| | | initDBPool, |
| | | checkLoginSession, uKeyLogin |
| | | checkLoginSession, uKeyLogin, getRandomFromServer, loginSignVerity |
| | | } from "@/assets/js/api"; |
| | | import ConfigInfo from "@/pages/configInfo"; |
| | | import UkeyBind from "@/components/UKeyBind"; |
| | |
| | | import qwh from '@/assets/images/qwh-logo.png'; |
| | | import yuanchange from '@/assets/images/yuanchang_logo2.png'; |
| | | import platform from "@/assets/js/config"; |
| | | import SoftUKey from "@/assets/js/tools/SoftUKey"; |
| | | import SoftKey3W from "@/assets/js/Syunew3"; |
| | | |
| | | export default { |
| | | components: { |
| | |
| | | platformName: "", |
| | | logoConfig: platform.logo, |
| | | copyRight: platform.copyRight, |
| | | softUKey: new SoftUKey(SoftKey3W) |
| | | }; |
| | | }, |
| | | created() { |
| | |
| | | } |
| | | |
| | | if (this.$CFG.uKey.value) { |
| | | this.uKeyLogin(); |
| | | this.getRandomFromServer(); |
| | | //this.uKeyLogin(); |
| | | } else { |
| | | this.normalLogin(); |
| | | } |
| | |
| | | this.$message.error("用户名或密码不能为空"); |
| | | } |
| | | }, |
| | | getRandomFromServer() { |
| | | getRandomFromServer().then(res=>{ |
| | | let rs = JSON.parse(res.data.result); |
| | | if(rs.code == 1) { |
| | | let randomNumber = rs.data; |
| | | this.softUKey.getSign(randomNumber, this.checkUKeySign); |
| | | }else { |
| | | this.$message.error("服务器连接异常!"); |
| | | } |
| | | }).catch(error=>{ |
| | | this.$message.error("服务器连接异常!"); |
| | | }); |
| | | }, |
| | | checkUKeySign(result) { |
| | | let reg = new RegExp("&", "g"); |
| | | result.inPath = result.inPath.replace(reg, "%26"); |
| | | result.inPath = result.inPath.replace(/\\/g, "huodongwei"); |
| | | loginSignVerity(result).then(res=>{ |
| | | let rs = JSON.parse(res.data.result); |
| | | if(rs.code == 1) { |
| | | this.uKeyLogin(); |
| | | }else { |
| | | this.$message.error("UKey签名校验失败!"); |
| | | } |
| | | }).catch(error=>{ |
| | | this.$message.error("服务器连接异常!"); |
| | | }); |
| | | }, |
| | | uKeyLogin() { |
| | | // |
| | | if (!this.$store.state.ukey.connect) { |
| | |
| | | version=UK_Data.return_value;//获得返回的UK的版本 |
| | | if(version>10) { |
| | | //取得锁的出厂编码 |
| | | s_simnew1.GetProduceDate( DevicePath);//发送命令取UK的出厂编码 |
| | | s_simnew1.GetProduceDate(DevicePath);//发送命令取UK的出厂编码 |
| | | }else |
| | | { |
| | | console.log("锁的版本少于11,不支持返回锁的出厂编码功能\n2013年9月15号以后出厂的锁都支持这个功能。"); |