| @@ -0,0 +1,3 @@ | |||
| NODE_ENV='development' | |||
| VUE_APP_SERVER_URL=https://localhost:8080 | |||
| TEST='88999' | |||
| @@ -0,0 +1,2 @@ | |||
| NODE_ENV='production' | |||
| VUE_APP_SERVER_URL=https://127.0.0.1 | |||
| @@ -0,0 +1,2 @@ | |||
| NODE_ENV='test' | |||
| VUE_APP_SERVER_URL=http://10.12.96.114:8082/ | |||
| @@ -0,0 +1,30 @@ | |||
| .DS_Store | |||
| node_modules | |||
| /dist | |||
| # local env files | |||
| .env.local | |||
| .env.*.local | |||
| # Log files | |||
| npm-debug.log* | |||
| yarn-debug.log* | |||
| yarn-error.log* | |||
| # Editor directories and files | |||
| .idea | |||
| .vscode | |||
| !.vscode/launch.json | |||
| !.vscode/settings.json | |||
| *.suo | |||
| *.ntvs* | |||
| *.njsproj | |||
| *.sln | |||
| *.sw? | |||
| .es* | |||
| .browserslistrc | |||
| .docker* | |||
| .npmrc | |||
| .prettierrc | |||
| .stylelintrc | |||
| @@ -0,0 +1,12 @@ | |||
| module.exports = { | |||
| presets: ["@vue/cli-plugin-babel/preset"], | |||
| plugins: [ | |||
| [ | |||
| "component", | |||
| { | |||
| libraryName: "mint-ui", | |||
| style: true | |||
| } | |||
| ] | |||
| ] | |||
| }; | |||
| @@ -0,0 +1,96 @@ | |||
| user www-data; | |||
| worker_processes 1; | |||
| error_log /var/log/nginx/error.log warn; | |||
| pid /var/run/nginx.pid; | |||
| events { | |||
| worker_connections 1024; | |||
| } | |||
| http { | |||
| include /etc/nginx/mime.types; | |||
| default_type application/octet-stream; | |||
| log_format main '$remote_addr($http_x_real_ip) - $remote_user [$time_local] ' | |||
| '$ssl_protocol/$ssl_cipher ' | |||
| '"$http_host($http_true_client_ip:$upstream_addr) $request" $status $body_bytes_sent ' | |||
| '"$http_referer" "$http_user_agent" "$http_x_forwarded_for"' | |||
| '\n'; | |||
| access_log /var/log/nginx/access.log main; | |||
| sendfile on; | |||
| keepalive_timeout 65; | |||
| gzip on; | |||
| gzip_static on; | |||
| gzip_vary on; | |||
| gzip_disable "MSIE [1-6]\."; | |||
| gzip_proxied expired no-cache no-store private auth; | |||
| gzip_comp_level 1; | |||
| gzip_min_length 10k; | |||
| gzip_types | |||
| text/plain | |||
| text/css | |||
| text/js | |||
| text/javascript | |||
| text/html | |||
| text/xml | |||
| text/x-component | |||
| application/javascript | |||
| application/x-javascript | |||
| application/json | |||
| application/xhtml+xml | |||
| application/xml | |||
| application/rss+xml | |||
| application/x-font-ttf | |||
| application/vnd.ms-fontobject | |||
| font/truetype | |||
| font/opentype | |||
| image/svg+xml svg svgz; | |||
| map $http_accept $webp_suffix { | |||
| default ""; | |||
| "~*webp" ".webp"; | |||
| } | |||
| map $msie $cache_control { | |||
| "1" "private"; | |||
| } | |||
| map $msie $vary_header { | |||
| default "Accept"; | |||
| "1" ""; | |||
| } | |||
| server { | |||
| listen 8888 ssl http2; | |||
| listen [::]:8888 ssl http2; | |||
| server_name test.cloudminds.com; | |||
| ssl_certificate /etc/nginx/cert/214971167760195.pem; | |||
| ssl_certificate_key /etc/nginx/cert/214971167760195.key; | |||
| ssl_session_cache shared:SSL:10m; | |||
| ssl_session_timeout 10m; | |||
| ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | |||
| ssl_ciphers HIGH:!aNULL:!MD5; | |||
| ssl_prefer_server_ciphers on; | |||
| location / { | |||
| root /srv/www/cloudia-fe-mall; | |||
| index index.html; | |||
| try_files $uri $uri/ /index.html; | |||
| } | |||
| location ~* .(jpe?g|png|gif|svg)$ { | |||
| root /srv/www/cloudia-fe-mall; | |||
| add_header Vary $vary_header; | |||
| add_header Cache-Control $cache_control; | |||
| try_files $uri$webp_suffix $uri =404; | |||
| } | |||
| error_page 500 502 503 504 /50x.html; | |||
| location = /50x.html { | |||
| root /usr/share/nginx/html; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| { | |||
| "name": "wh-aiman", | |||
| "version": "0.0.2", | |||
| "private": true, | |||
| "scripts": { | |||
| "serve": "vue-cli-service serve --open", | |||
| "build": "vue-cli-service build --modern --mode development", | |||
| "build:test": "vue-cli-service build --modern --mode test", | |||
| "build:prod": "vue-cli-service build --modern --mode production", | |||
| "lint": "vue-cli-service lint --no-fix", | |||
| "lint:fix": "vue-cli-service lint", | |||
| "analyze": "cross-env ANALYZER=true npm run build:prod && webpack-bundle-analyzer --port 8123 dist/stats.json" | |||
| }, | |||
| "dependencies": { | |||
| "axios": "^0.19.2", | |||
| "core-js": "^3.6.4", | |||
| "element-ui": "^2.15.10", | |||
| "js-md5": "^0.7.3", | |||
| "jsencrypt": "^3.2.1", | |||
| "less-loader": "^11.1.0", | |||
| "lodash": "^4.17.21", | |||
| "mint-ui": "^2.2.13", | |||
| "moment": "^2.29.4", | |||
| "vue": "^2.6.11", | |||
| "vue-grid-layout": "^2.3.7", | |||
| "vue-lazyload": "^1.3.3", | |||
| "vue-navigation": "^1.1.4", | |||
| "vue-router": "^3.1.6", | |||
| "vuex": "^3.6.2" | |||
| }, | |||
| "devDependencies": { | |||
| "@amap/amap-jsapi-loader": "0.0.1", | |||
| "@vue/cli-plugin-babel": "~4.3.0", | |||
| "@vue/cli-plugin-eslint": "~4.3.0", | |||
| "@vue/cli-plugin-router": "~4.3.0", | |||
| "@vue/cli-plugin-vuex": "~4.3.0", | |||
| "@vue/cli-service": "~4.3.0", | |||
| "@vue/eslint-config-prettier": "^6.0.0", | |||
| "babel-eslint": "^10.1.0", | |||
| "babel-plugin-component": "^1.1.1", | |||
| "babel-plugin-lodash": "^3.3.4", | |||
| "compression-webpack-plugin": "^3.1.0", | |||
| "copy-webpack-plugin": "^6.0.2", | |||
| "cross-env": "^7.0.2", | |||
| "eslint": "^6.7.2", | |||
| "eslint-plugin-prettier": "^3.1.1", | |||
| "eslint-plugin-vue": "^6.2.2", | |||
| "imagemin-mozjpeg": "^8.0.0", | |||
| "imagemin-webp-webpack-plugin": "^3.3.1", | |||
| "imagemin-webpack-plugin": "^2.4.2", | |||
| "jquery": "^3.6.1", | |||
| "lodash-webpack-plugin": "^0.11.6", | |||
| "prettier": "^1.19.1", | |||
| "sass": "^1.26.3", | |||
| "sass-loader": "^8.0.2", | |||
| "vue-awesome-swiper": "^3.1.3", | |||
| "vue-template-compiler": "^2.6.11", | |||
| "webpack-bundle-analyzer": "^3.9.0" | |||
| } | |||
| } | |||
| @@ -0,0 +1,192 @@ | |||
| { | |||
| "ttsControlEnable": true, | |||
| "chatEnable": false, | |||
| "showMsg": false, | |||
| "autoOpenDoor": true, | |||
| "ableHandleLight": true, | |||
| "debug": false, | |||
| "detectedFaceEnable": false, | |||
| "handleUrl": "http://192.168.1.146:8080/", | |||
| "text_match": { | |||
| "工区": "公区", | |||
| "奥比": "奥北", | |||
| "回忆": "会议", | |||
| "宝贝": "报备", | |||
| "卡门": "开门", | |||
| "看门": "开门", | |||
| "看看": "开门", | |||
| "早下": "找下", | |||
| "可燃": "客人", | |||
| "克然": "客人", | |||
| "早一下": "找一下" | |||
| }, | |||
| "clothes": { | |||
| "SweetGirl": [ | |||
| "suit_blue", | |||
| "suit_red", | |||
| "waistcoat_blue" | |||
| ], | |||
| "BusinessGirl": [ | |||
| "suit_green", | |||
| "waistcoat_black" | |||
| ] | |||
| }, | |||
| "characters": [ | |||
| "SweetGirl", | |||
| "BusinessGirl" | |||
| ], | |||
| "zhaohu": [ | |||
| "你好", | |||
| "您好", | |||
| "小酷", | |||
| "小酷小酷", | |||
| "你好小酷", | |||
| "hello" | |||
| ], | |||
| "breakReqs": [ | |||
| "停", | |||
| "闭嘴", | |||
| "住嘴", | |||
| "stop", | |||
| "shut up" | |||
| ], | |||
| "helpReqs": [ | |||
| "我找", | |||
| "我来", | |||
| "在嘛", | |||
| "这里是", | |||
| "来找", | |||
| "送快递", | |||
| "送外卖", | |||
| "卢总", | |||
| "王总" | |||
| ], | |||
| "serverDb": "http://192.168.1.254:13000", | |||
| "nuoDiSrvUrl": "http://39.105.85.176:48080/nuodi", | |||
| "nuoDiAppId": "759a993c", | |||
| "nuoDiSecret": "3946401cbcc24a90b208", | |||
| "serverUrl": "https://ai.lecooai.com", | |||
| "notifyTodayUrl": "/v1/notifyToday", | |||
| "returnAdPage": 30000, | |||
| "judgeDetectedNoPerson": 5000, | |||
| "judgeNoPerson": 15000, | |||
| "normal": [ | |||
| ], | |||
| "greeting": [ | |||
| "您好", | |||
| "嗨", | |||
| "Hi", | |||
| "Hello" | |||
| ], | |||
| "progressMap": { | |||
| "dhys":"会议室模式", | |||
| "fkbb": "访客预约", | |||
| "fkdj": "访客登记", | |||
| "moshengren": "陌生人" | |||
| }, | |||
| "lights": [ | |||
| "会议", | |||
| "门口", | |||
| "公区", | |||
| "会客", | |||
| "大门" | |||
| ], | |||
| "meetings": [ | |||
| "会议", | |||
| "开会", | |||
| "约会", | |||
| "定会", | |||
| "约个会", | |||
| "预约", | |||
| "预定", | |||
| "会议预定" | |||
| ], | |||
| "visits": [ | |||
| "访客","访客报备","访客预约","客人","客人到访","报备" | |||
| ], | |||
| "visitors":["我找","在么","在吗","外卖","登记","送快递","面试","找", "见", "访","预约", "来找", "找一下","找下", "客人", "有约", "预约"], | |||
| "forwards": ["对","好","ok","可以","是","嗯","行","需要","要"], | |||
| "opposites": ["不","否","no"], | |||
| "meetingTopics": ["例会", "事项讨论", "访客接待","面试", "自定义"], | |||
| "lights_match": { | |||
| "会议": 0, | |||
| "会议室": 0, | |||
| "展厅": 1, | |||
| "门口": 1, | |||
| "工区": 2, | |||
| "办公区": 2, | |||
| "会客室": 3, | |||
| "会客": 3, | |||
| "东屋": 3, | |||
| "大门": 4 | |||
| }, | |||
| "workLights": [ | |||
| { | |||
| "deviceId": "21", | |||
| "channel": "2" | |||
| },{ | |||
| "deviceId": "21", | |||
| "channel": "4" | |||
| },{ | |||
| "deviceId": "21", | |||
| "channel": "1" | |||
| },{ | |||
| "deviceId": "21", | |||
| "channel": "3" | |||
| } | |||
| ], | |||
| "lightsObj":[ | |||
| { | |||
| "deviceId": "20", | |||
| "channel": "2" | |||
| },{ | |||
| "deviceId": "20", | |||
| "channel": "1" | |||
| }], | |||
| "devMappings": [ | |||
| { | |||
| "robotCode": "864972045000291", | |||
| "title": "阳光恒昌", | |||
| "deviceId": "ROBOT_PAD1", | |||
| "openDoorUrl": "http://192.168.1.253:9091", | |||
| "officeCode": "6123731313956274818", | |||
| "defaultPhones": "13488821068,15001249652", | |||
| "asks": { | |||
| "mgr": "", | |||
| "emp": "", | |||
| "emp2": "", | |||
| "keyVisitor": "欢迎来到阳光恒昌,请进。", | |||
| "keyVisitor2": "", | |||
| "visitor": "好久不见,您先在会议室稍坐,已通知到我的同事", | |||
| "visitor2": "", | |||
| "courier": "快递外卖人员暂不可进入本公司,请您在门外联系相关人员。", | |||
| "blackRole": "你是黑名单人员,不允许进入本公司,请赶快离开!", | |||
| "default": "我是前台小酷,有什么可以帮您?" | |||
| } | |||
| },{ | |||
| "robotCode": "864972045000846", | |||
| "title": "缔智元", | |||
| "deviceId": "ROBOT_PAD2", | |||
| "openDoorUrl": "/srv/api/passDoor?deviceId=VGATE1", | |||
| "officeCode": "3969026645953309806", | |||
| "defaultPhones": "18910801519,19306342044", | |||
| "asks": { | |||
| "mgr": "", | |||
| "emp": "", | |||
| "emp2": "", | |||
| "keyVisitor": "欢迎来到缔智元,请进。", | |||
| "keyVisitor2": "", | |||
| "visitor": "好久不见,您先在会议室稍坐,已通知到我的同事", | |||
| "visitor2": "", | |||
| "courier": "快递外卖人员暂不可进入本公司,请您在门外联系相关人员。", | |||
| "blackRole": "你是黑名单人员,不允许进入本公司,请赶快离开!", | |||
| "default": "我是前台小酷,有什么可以帮您?" | |||
| } | |||
| } | |||
| ] | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| <meta http-equiv="pragma" content="no-cache" /> | |||
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |||
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
| <meta http-equiv="x-dns-prefetch-control" content="on" /> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |||
| <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | |||
| <style> | |||
| body { | |||
| height: 100% !important; | |||
| background-color: transparent !important; | |||
| } | |||
| .dg-main-content { | |||
| max-width: 300px !important; | |||
| } | |||
| .other { | |||
| color: red; | |||
| } | |||
| .dg-content{ | |||
| text-align: center; | |||
| } | |||
| .dg-content-body { | |||
| border-bottom: none !important; | |||
| padding: 15px 0 !important; | |||
| } | |||
| .el-message-box { | |||
| width: 92vw !important; | |||
| } | |||
| </style> | |||
| <title><%= htmlWebpackPlugin.options.title %></title> | |||
| </head> | |||
| <body> | |||
| <noscript> | |||
| <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | |||
| </noscript> | |||
| <div id="app"></div> | |||
| <!-- built files will be auto injected --> | |||
| </body> | |||
| <script scr="@/assets/bootstrap.min.js"></script> | |||
| </html> | |||
| @@ -0,0 +1,432 @@ | |||
| <template> | |||
| <div id="app" class="no-scrollbar" style="width: 100%;"> | |||
| <!-- <div | |||
| v-show="(this.ws && this.ws.readyState == 1) || !this.ws" | |||
| style="position: absolute;background: orangered;width: 2vw;height: 2vh;right: 0" | |||
| ></div>--> | |||
| <div v-if="debug" class="record-wrapper" style="position: absolute;top: 0;z-index: 999;"> | |||
| <div class="msg msg-right" style="display: contents;"> | |||
| <div | |||
| :key="this.consoleList.index" | |||
| class="message-wrapper-right" | |||
| style="width:90%;display: flex;background-color: rgba(220,220,220,0.7) !important;border-radius: 2px !important;" | |||
| > | |||
| <div class="message" style="font-size: 5px !important;text-align: left !important;"> | |||
| {{ this.consoleList && this.consoleList.time ? this.consoleList.time + ":" : "" }}:{{ | |||
| (this.consoleList ? this.consoleList.message : "") + | |||
| "(pg:" + | |||
| this.lastProgress + | |||
| ";sleep:" + | |||
| this.sleep + | |||
| ";speak:" + | |||
| this.isSpeaking + | |||
| ";state:" + | |||
| this.stateVal + | |||
| ";npl:" + | |||
| this.lastNplText + | |||
| ";tts:" + | |||
| this.lastTtsText + | |||
| ";q:" + | |||
| this.lastQText + | |||
| ";locked:" + | |||
| this.execLocked + | |||
| ";qHandle:" + | |||
| this.qHandleType + | |||
| ")" + | |||
| this.otherLog + | |||
| JSON.stringify(this.lastPerson) + | |||
| this.reqResult + | |||
| "noDetectedFace:" + | |||
| this.noDetectedFace() + | |||
| "noPersonHere:" + | |||
| this.noPersonHere() | |||
| }} | |||
| </div> | |||
| <button class="btn" style="position: absolute;top:12vh;" @click="sendMsg(1)"> | |||
| TEST | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <navigation> | |||
| <router-view :style="content"> </router-view> | |||
| </navigation> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import Cloudia from "./api/cloudia-sdk-v1.4.1"; | |||
| import { handleFaceType } from "./utils/handleTts"; | |||
| import moment from "moment"; | |||
| import { mapMutations, mapState } from "vuex"; | |||
| var initTimer2 = 0; | |||
| export default { | |||
| name: "App", | |||
| data() { | |||
| return { | |||
| isRouterAlive: true, | |||
| interactive: "", | |||
| content: "", | |||
| retryLoadClientType: 0, | |||
| lastDetectTs: 0, | |||
| lastPersonTs: 0, | |||
| lastPerson: {}, | |||
| sockData: {}, | |||
| lastNplText: "", | |||
| lastTtsText: "", | |||
| lastMsDetectTs: 0, | |||
| lastMsNlpTimes: 0, | |||
| lastDetectObj: {}, | |||
| lastQText: "", | |||
| lastQTs: 0, | |||
| execLocked: "", | |||
| qHandleType: "", | |||
| reqResult: "", | |||
| otherLog: "", | |||
| otherLog2: "", | |||
| sleep: false, | |||
| isSpeaking: false, | |||
| cameraMode: "level", | |||
| jsHandleStateEnabled: false, | |||
| jsPlayTtsEnabled: true, | |||
| stateVal: "", | |||
| ws: null, | |||
| times: 0, | |||
| timeoutObj: null, | |||
| serverTimeoutObj: null, | |||
| timeout: 10000, | |||
| qData: {}, | |||
| nlpData: {}, | |||
| rMsg: {}, | |||
| heartCheck: { | |||
| reset: this.reset, | |||
| start: this.start | |||
| }, | |||
| consoleList: {}, | |||
| debug: this.$cmdList.debug, | |||
| connectTimer: null, | |||
| strangerDetectTimes: 0 | |||
| }; | |||
| }, | |||
| computed: { | |||
| ...mapState({ | |||
| lastProgress: "lastReq", | |||
| padInfo: "pad", | |||
| lPerson: "lastPerson", | |||
| localDev: "localDevInfo", | |||
| debugFlag: "debug" | |||
| }) | |||
| }, | |||
| watch: { | |||
| padInfo: { | |||
| handler: function(val, oldVal) { | |||
| let that = this; | |||
| if (val && that.$cmdList.detectedFaceEnable) { | |||
| that.wsInit(); | |||
| } | |||
| }, | |||
| deep: true | |||
| } | |||
| }, | |||
| mounted() { | |||
| window._ = _; | |||
| window.globalVue = this.$children[0]; | |||
| window.timer = undefined; | |||
| window.intimer = undefined; | |||
| window.socketTimer = undefined; | |||
| window.closeTimer = undefined; | |||
| window.Cloudia = Cloudia; | |||
| }, | |||
| destroyed() { | |||
| // console.log("=========app destroyed===="); | |||
| }, | |||
| methods: { | |||
| ...mapMutations([ | |||
| "setLastPerson", | |||
| "setLastReq", | |||
| "setProgressPerson", | |||
| "setLocalDevInfo", | |||
| "setRobotMsg", | |||
| "setWs" | |||
| ]), | |||
| reset: function() { | |||
| this.timeoutObj && clearTimeout(this.timeoutObj); | |||
| this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj); | |||
| return this; | |||
| }, | |||
| start: function() { | |||
| let self = this; | |||
| this.timeoutObj = setTimeout(function() { | |||
| self.ws.send("ping"); | |||
| self.serverTimeoutObj = setTimeout(function() { | |||
| self.ws.close(); | |||
| }, self.timeout); | |||
| }, this.timeout); | |||
| }, | |||
| consoleLog(text) { | |||
| let ts = new Date().getTime(); | |||
| this.consoleList = { | |||
| index: ts, | |||
| message: text, | |||
| time: moment(ts).format("mm:ss.SSS") | |||
| }; | |||
| }, | |||
| setProgressCode(key) { | |||
| this.setLastReq(key); | |||
| }, | |||
| sendMsg(type) { | |||
| var that = this; | |||
| that.setLastReq("dhys"); | |||
| }, | |||
| returnAdPage() { | |||
| // this.sleep = true; | |||
| this.sleep = false; | |||
| this.setLastReq(""); | |||
| this.isSpeaking = false; | |||
| this.meetingDateStr = ""; | |||
| this.meetingTitle = ""; | |||
| // this.lastMsNlpTimes = 0; | |||
| // this.lastMsDetectTs = 0; | |||
| this.strangerDetectTimes = 0; | |||
| this.msgList = [ | |||
| { | |||
| from: 2, | |||
| message: "您好,我是小酷。" | |||
| } | |||
| ]; | |||
| window.Cloudia && window.Cloudia.setClothes("suit_blue"); | |||
| }, | |||
| leaveAdPage() { | |||
| this.sleep = false; | |||
| // window.Cloudia && window.Cloudia.setClothes("suit_blue"); | |||
| }, | |||
| wsInit() { | |||
| var that = this; | |||
| if (this.padInfo && this.padInfo.ipAddr) { | |||
| // let wsUrl = "ws://127.0.0.1:9090"; | |||
| let wsUrl = "ws://" + this.padInfo.ipAddr + ":9090"; | |||
| //先判断浏览器是否支持WebSocket | |||
| if (typeof WebSocket === "undefined") { | |||
| that.$alert("您的浏览器不支持socket"); | |||
| } else { | |||
| // 判断 WebSocket是否已经建立连接,避免重复连接 | |||
| // this.ws && this.ws.close(); | |||
| // 新增一个websocket实例,并且建立连接 | |||
| this.ws = new WebSocket(wsUrl); | |||
| // 连接成功后的回调函数 | |||
| this.ws.onopen = this.handleWsOpen; | |||
| // 收到服务器数据后的回调函数 | |||
| this.ws.onmessage = this.handleWsMessage; | |||
| // 连接关闭后的回调函数 | |||
| this.ws.onclose = this.handleReconnectWs; | |||
| // 报错时的回调函数(网络断开,网络不稳定, 用户电脑休眠) | |||
| this.ws.onerror = this.handleReconnectWs; | |||
| this.setWs(this.ws); | |||
| } | |||
| } else { | |||
| that.handleReconnectWs(); | |||
| } | |||
| }, | |||
| // ws建立连接 | |||
| handleWsOpen() { | |||
| this.consoleLog("websocket已连接,初始化成功"); | |||
| this.heartCheck.reset().start(); | |||
| }, | |||
| // ws拿到服务器(后端接口)或者客户端(心跳监测或者自测)发送的数据 | |||
| handleWsMessage(msg) { | |||
| this.heartCheck.reset().start(); | |||
| // 拿到的数据 | |||
| let result = msg.data; | |||
| let that = this; | |||
| if (result == "tong") { | |||
| return; | |||
| } | |||
| that.lastDetectTs = new Date().getTime(); | |||
| that.sockData = JSON.parse(result); | |||
| that.consoleLog(JSON.stringify(that.sockData)); | |||
| if (!that.isSpeaking && (that.lastProgress == "" || that.lastProgress == "moshengren")) { | |||
| that.handleDetectedFace(that.sockData); | |||
| } | |||
| }, | |||
| // 重连ws | |||
| handleReconnectWs() { | |||
| this.connectTimer && clearTimeout(this.connectTimer); | |||
| let that = this; | |||
| that.consoleLog("socket断开链接"); | |||
| that.times++; | |||
| this.connectTimer = setTimeout(() => { | |||
| if (that.ws) { | |||
| that.consoleLog("重连" + that.times + "次", that.ws.readyState); | |||
| // 接已经关闭,或者打开连接失败 | |||
| if (that.ws.readyState === 3) { | |||
| that.wsInit(); | |||
| } | |||
| } else { | |||
| that.consoleLog("重连" + that.times + "次"); | |||
| that.wsInit(); | |||
| } | |||
| }, 3000); | |||
| }, | |||
| handleDetectedFace(obj) { | |||
| let that = this; | |||
| let str = ""; | |||
| if (obj && (obj.id == undefined || obj.id == -1)) { | |||
| //识别到脸,不是系统里的合法人脸 | |||
| // let currentTs = new Date().getTime(); | |||
| // that.lastDetectTs = currentTs; //识别到脸的时间戳 | |||
| // that.lastMsDetectTs = that.lastMsDetectTs == 0 ? currentTs : that.lastMsDetectTs; //陌生人识别时间戳 | |||
| that.leaveAdPage(); | |||
| //陌生人识别3秒,只提示一次 | |||
| if (that.strangerDetectTimes > 5) { | |||
| // that.$alert("moshengren"); | |||
| // that.strangerDetectTimes = 0; | |||
| that.lastDetectObj = obj; | |||
| that.lastPerson = {}; | |||
| that.setLastPerson({}); | |||
| // that.lastMsDetectTs = 0; | |||
| str += handleFaceType(that, obj); | |||
| that.sleep = false; | |||
| that.rMsg = { text: str, ts: new Date().getTime() }; | |||
| that.setRobotMsg(that.rMsg); | |||
| // that.lastMsNlpTimes++; | |||
| } else { | |||
| that.strangerDetectTimes++; | |||
| } | |||
| } else if (obj && (obj.id.length > 6 || obj.id > 0)) { | |||
| that.strangerDetectTimes = 0; | |||
| //识别到认识的人 | |||
| // that.lastMsDetectTs = 0; | |||
| let currentTs = new Date().getTime(); | |||
| that.lastDetectTs = currentTs; | |||
| that.lastDetectObj = obj; | |||
| //识别到人 | |||
| /*if (obj.name != "") { | |||
| if (that.lastProgress == "moshengren") { | |||
| // that.setLastReq(""); | |||
| } | |||
| }*/ | |||
| /*if (that.lastProgress == "moshengren") { | |||
| return; | |||
| }*/ | |||
| if (that.lastDetectObj.id === that.lastPerson.id) { | |||
| that.lastPerson.name = that.lastDetectObj.name; | |||
| that.lastPerson.tag = that.lastDetectObj.tag; | |||
| } | |||
| //1分钟识别到同一人只执行一次 | |||
| if ( | |||
| (obj.id == that.lastPerson.id && (currentTs - that.lastPersonTs >= 60000 || that.sleep)) || | |||
| obj.id != that.lastPerson.id | |||
| ) { | |||
| // that.lastMsNlpTimes = 0; | |||
| that.lastPerson = obj; | |||
| that.setLastPerson(obj); | |||
| that.lastPersonTs = currentTs; | |||
| str += handleFaceType(that, obj); | |||
| that.rMsg = { text: str, ts: new Date().getTime() }; | |||
| if (that.lastProgress == "") { | |||
| that.setRobotMsg(that.rMsg); | |||
| } | |||
| that.sleep = false; | |||
| that.addMsgList(2, str); | |||
| } else { | |||
| that.consoleLog("repeat person:" + obj.id); | |||
| } | |||
| } | |||
| }, | |||
| noDetectedFace() { | |||
| let currentTs = new Date().getTime(); | |||
| if (currentTs - this.lastDetectTs > this.$cmdList.judgeDetectedNoPerson) { | |||
| //5秒内没识别 | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| noPersonHere() { | |||
| let currentTs = new Date().getTime(); | |||
| if (currentTs - this.lastQTs > this.$cmdList.judgeNoPerson) { | |||
| //15秒钟不说话 | |||
| return this.noDetectedFace(); | |||
| } | |||
| return false; | |||
| }, | |||
| setIsSpeaking(speak) { | |||
| this.isSpeaking = speak; | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style lang="scss"> | |||
| * { | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| html, | |||
| body { | |||
| width: 100%; | |||
| height: 100%; | |||
| overflow-x: hidden; | |||
| overflow-y: hidden; | |||
| } | |||
| .record-wrapper { | |||
| margin: 4px; | |||
| padding: 4px; | |||
| width: 90vw; | |||
| } | |||
| #app { | |||
| width: 100%; | |||
| height: 100%; | |||
| font-family: Avenir, Helvetica, Arial, sans-serif; | |||
| -webkit-font-smoothing: antialiased; | |||
| -moz-osx-font-smoothing: grayscale; | |||
| text-align: center; | |||
| color: #2c3e50; | |||
| } | |||
| // 隐藏滚轮 | |||
| .no-scrollbar { | |||
| -ms-overflow-style: none; | |||
| overflow: -moz-scrollbars-none; | |||
| /*background-color: black;*/ | |||
| -webkit-touch-callout: none; | |||
| -webkit-user-select: none; | |||
| -khtml-user-select: none; | |||
| -moz-user-select: none; | |||
| -ms-user-select: none; | |||
| user-select: none; | |||
| } | |||
| .no-scrollbar::-webkit-scrollbar { | |||
| width: 0 !important; | |||
| } | |||
| img { | |||
| width: auto; | |||
| height: auto; | |||
| max-width: 100%; | |||
| max-height: 100%; | |||
| } | |||
| img[lazy="loading"] { | |||
| background-color: white; | |||
| } | |||
| .single-line-text { | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||
| white-space: nowrap; | |||
| display: block !important; | |||
| text-align: left; | |||
| -webkit-line-clamp: 1; | |||
| -webkit-box-orient: vertical; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,452 @@ | |||
| export default { | |||
| /** | |||
| * 初始化SDK 传入onReceiveCmd, 用于接收nlp, a, reload等命令 | |||
| * @param onReceiveCmd | |||
| * @param config | |||
| */ | |||
| init( | |||
| onReceiveCmd, | |||
| config = { | |||
| jsHandleTts: false, | |||
| jsHandleState: false | |||
| } | |||
| ) { | |||
| window.sendCmd = function(cmd, params) { | |||
| if (cmd === "onBind") { | |||
| ue && ue.cloudminds.hue("onstart", JSON.stringify(config)); | |||
| console.info("onStart event sent"); | |||
| window.cloudiaConfig = params; | |||
| if (params && params.SweetGirlClothes) { | |||
| cloudiaConfig.girls = new Map(); | |||
| cloudiaConfig.sweetGirlClothes = params.SweetGirlClothes.replace(" ", "").split(","); | |||
| cloudiaConfig.girls.set("SweetGirl", cloudiaConfig.sweetGirlClothes); | |||
| cloudiaConfig.allClothes = cloudiaConfig.girls.get("SweetGirl"); | |||
| if (params.BusinessGirlClothes) { | |||
| cloudiaConfig.businessGirlClothes = params.BusinessGirlClothes.replace(" ", "").split(","); | |||
| cloudiaConfig.girls.set("BusinessGirl", cloudiaConfig.businessGirlClothes); | |||
| } | |||
| if (params.AncientGirlClothes) { | |||
| cloudiaConfig.ancientGirlClothes = params.AncientGirlClothes.replace(" ", "").split(","); | |||
| cloudiaConfig.girls.set("AncientGirl", cloudiaConfig.ancientGirlClothes); | |||
| } | |||
| } | |||
| } | |||
| return onReceiveCmd(cmd, params); | |||
| }; | |||
| }, | |||
| reloadClothes() { | |||
| if (cloudiaConfig && cloudiaConfig.SweetGirlClothes) { | |||
| cloudiaConfig.allClothes = cloudiaConfig.girls.get(cloudiaConfig.currentCharacter); | |||
| } | |||
| }, | |||
| isNotValidNum(value) { | |||
| return !/(^[\-0-9][0-9]*(.[0-9]+)?)$/.test(value); | |||
| }, | |||
| isEmpty(obj) { | |||
| return typeof obj === "undefined" || obj == null || obj === ""; | |||
| }, | |||
| /** | |||
| * 显示toast | |||
| * @param msg toast内容 | |||
| */ | |||
| showToast(msg) { | |||
| if (ue && ue.cloudminds) { | |||
| if (this.isEmpty(msg)) { | |||
| console.warn("Invalid call:showToast"); | |||
| return false; | |||
| } | |||
| ue.cloudminds.hue("showtoast", msg); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 显示长toast | |||
| * @param msg toast内容 | |||
| */ | |||
| showLongToast(msg) { | |||
| if (ue && ue.cloudminds) { | |||
| if (this.isEmpty(msg)) { | |||
| console.warn("Invalid call:showLongToast"); | |||
| return false; | |||
| } | |||
| ue.cloudminds.hue("showlongtoast", msg); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 语音播报 | |||
| * @param text 播报的内容 | |||
| */ | |||
| playTts(text) { | |||
| if (ue && ue.cloudminds) { | |||
| if (this.isEmpty(text)) { | |||
| console.warn("Invalid call:playTts"); | |||
| return false; | |||
| } | |||
| ue.cloudminds.hue("playtts", text); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 修改浏览器位置 | |||
| * @param left 浏览器距离屏幕左边的位置 | |||
| * @param top 浏览器距离屏幕上边的位置 | |||
| * @param right 浏览器距离屏幕右边的位置 | |||
| * @param bottom 浏览器距离屏幕下边的位置 | |||
| */ | |||
| updateBrowserLocation(left, top, right, bottom) { | |||
| if ( | |||
| this.isNotValidNum(left) || | |||
| this.isNotValidNum(top) || | |||
| this.isNotValidNum(right) || | |||
| this.isNotValidNum(bottom) | |||
| ) { | |||
| console.error("updateBrowserLocation 数据不合法"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| // 蓝图端用的是","分隔方案 | |||
| ue.cloudminds.hue("updatebrowserlocation", left + ", " + top + ", " + right + ", " + bottom); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 修改虚拟人物位置 | |||
| * @param locationX X坐标值 | |||
| * @param locationY Y坐标值 | |||
| * @param locationZ Z坐标值 | |||
| * @param persistence 是否持久化保存 | |||
| */ | |||
| updateCharacterLocation(locationX, locationY, locationZ, persistence = false) { | |||
| if (this.isNotValidNum(locationX) || this.isNotValidNum(locationY) || this.isNotValidNum(locationZ)) { | |||
| console.error("updateCharacterLocation 数据不合法"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue( | |||
| "updatecharacterlocation", | |||
| locationX + "#$*&@" + locationY + "#$*&@" + locationZ + "#$*&@" + persistence | |||
| ); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 修改虚拟人物旋转角度 | |||
| * @param rotationP P坐标值 | |||
| * @param rotationY Y坐标值 | |||
| * @param rotationR R坐标值 | |||
| * @param persistence 是否持久化保存 | |||
| */ | |||
| updateCharacterRotation(rotationP, rotationY, rotationR, persistence = false) { | |||
| if (this.isNotValidNum(rotationP) || this.isNotValidNum(rotationY) || this.isNotValidNum(rotationR)) { | |||
| console.error("updateCharacterRotation 数据不合法"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue( | |||
| "updatecharacterrotation", | |||
| rotationP + "#$*&@" + rotationY + "#$*&@" + rotationR + "#$*&@" + persistence | |||
| ); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 播放动画 | |||
| * @param name 动画名称 | |||
| * @param status 动画状态 | |||
| */ | |||
| playMotion(name, status = "") { | |||
| if (ue && ue.cloudminds) { | |||
| if (this.isEmpty(name)) { | |||
| console.warn("Invalid call:playMotion"); | |||
| return false; | |||
| } | |||
| if (status.length === 0) { | |||
| ue.cloudminds.hue("playmotion", name); | |||
| } else { | |||
| ue.cloudminds.hue("playmotion", name + "#$*&@" + status); | |||
| } | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 设置表情 | |||
| * @param exp 表情名称 | |||
| * @param value 值 | |||
| */ | |||
| setExpression(exp, value) { | |||
| if (ue && ue.cloudminds) { | |||
| if (this.isEmpty(value) || this.isEmpty(exp)) { | |||
| console.warn("Invalid call:setExpression"); | |||
| return false; | |||
| } | |||
| ue.cloudminds.hue("setexpression", exp + "#$*&@" + value); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * @param color 背景墙颜色 | |||
| */ | |||
| setBgWallColor(color) { | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setMapBgColor", color); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * @param wall 背景墙资源名称 | |||
| */ | |||
| setBgWallResource(wall) { | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setMapBgResource", wall); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 设置是否由js接管tts播报,接管之后,app将不负责播报,转而由js负责调用palytts进行播报 | |||
| * @param enable | |||
| */ | |||
| setJsPlayTtsEnabled(enable) { | |||
| if (this.isEmpty(enable)) { | |||
| console.warn("Invalid call:setJsPlayTtsEnabled"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setjsplayttsenabled", enable.toString()); | |||
| } | |||
| return true; | |||
| }, | |||
| /** | |||
| * 设置是否由js接管state,接管之后,app将不切换state | |||
| * @param enable | |||
| */ | |||
| setJsHandleStateEnabled(enable) { | |||
| if (this.isEmpty(enable)) { | |||
| console.warn("Invalid call:setJsHandleStateEnabled"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setJsHandleStateEnabled", enable.toString()); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| /** | |||
| * 获取deviceId,调用成功之后会在onCmdReceived中收到deviceId的命令 | |||
| * @deprecated 请改用getRobotInfoConfig("RobotId")方法 | |||
| */ | |||
| getDeviceId() { | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("getdeviceid"); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| getRobotInfoConfig(key) { | |||
| if (ue && ue.cloudminds) { | |||
| if (this.isEmpty(key)) { | |||
| console.warn("Invalid call:getRobotInfoConfig"); | |||
| return false; | |||
| } | |||
| return ue.cloudminds.hueget("getrobotinfoconfig", key); | |||
| } | |||
| return new Promise(function() { | |||
| return ""; | |||
| }); | |||
| }, | |||
| getGreetingMsg() { | |||
| return this.getAppConfig(); | |||
| }, | |||
| getAppConfig() { | |||
| if (ue && ue.cloudminds) { | |||
| return ue.cloudminds.hueget("getappconfig"); | |||
| } | |||
| return new Promise(function() { | |||
| return ""; | |||
| }); | |||
| }, | |||
| getCharacter() { | |||
| if (ue && ue.cloudminds) { | |||
| return ue.cloudminds.hueget("getcharacterinfo"); | |||
| } | |||
| return new Promise(function() { | |||
| return ""; | |||
| }); | |||
| }, | |||
| setCharacter(cname) { | |||
| if (this.isEmpty(cname)) { | |||
| console.warn("Invalid call:setCharacter"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setcharacter", cname); | |||
| if (cloudiaConfig) { | |||
| cloudiaConfig.currentCharacter = cname; | |||
| } | |||
| this.reloadClothes(); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| setClothes(name) { | |||
| if (cloudiaConfig && cloudiaConfig.allClothes && !cloudiaConfig.allClothes.indexOf(name) < 0) { | |||
| console.warn("Invalid call:setClothes:" + name); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setClothes", name); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| postMediaEvent(event) { | |||
| if (this.isEmpty(event)) { | |||
| console.warn("Invalid call:postMediaEvent"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("postmediaevent", event); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| setAsrWakeUpEnable(enable) { | |||
| if (this.isEmpty(enable)) { | |||
| console.warn("Invalid call:setAsrWakeUpEnable"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setAsrWakeUpEnable", enable.toString()); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| stopPlayTts(playOK = false) { | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("stopTts", playOK.toString()); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| setWalkGreetingEnable(enable) { | |||
| if (this.isEmpty(enable)) { | |||
| console.warn("Invalid call:setWalkGreetEnable"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("walkGreetEnable", enable.toString()); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| setWallLocation(location) { | |||
| if (this.isEmpty(location)) { | |||
| console.warn("Invalid call:setWallLocation"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setbgwalllocation", location); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| enterImmerseMode(immerse) { | |||
| if (this.isEmpty(immerse)) { | |||
| console.warn("Invalid call:enterImmerseMode"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("enterImmerseMode", immerse.toString()); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| startVideoTalk() { | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("starttalk"); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| stopVideoTalk() { | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("endtalk"); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| textTriggerNlp(text) { | |||
| if (this.isEmpty(text)) { | |||
| console.warn("Invalid call:textTriggerNlp"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("texttriggernlp", text); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| setVolume(vol) { | |||
| if (this.isNotValidNum(vol)) { | |||
| console.warn("Invalid call:setVolume:" + vol); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setvolume", vol.toString()); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| getVolume() { | |||
| if (ue && ue.cloudminds) { | |||
| return ue.cloudminds.hueget("getcurvolume"); | |||
| } | |||
| return new Promise(function() { | |||
| return "11"; | |||
| }); | |||
| }, | |||
| downloadFiles(tag, urls) { | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("downloadfiles", tag + "#$*&@" + urls); | |||
| return true; | |||
| } | |||
| return false; | |||
| }, | |||
| updateCharacterScaleLevel(scaleLevel, persistence = false) { | |||
| if (this.isNotValidNum(scaleLevel)) { | |||
| console.warn("Invalid call:setVolume:" + persistence); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setcharacterscale", scaleLevel + "#$*&@" + persistence); | |||
| return true; | |||
| } | |||
| }, | |||
| restartH5() { | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("reopenbrowser"); | |||
| } | |||
| }, | |||
| setState(state) { | |||
| if (this.isEmpty(state)) { | |||
| console.warn("Invalid call:setState"); | |||
| return false; | |||
| } | |||
| if (ue && ue.cloudminds) { | |||
| ue.cloudminds.hue("setState", state); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| }; | |||
| @@ -0,0 +1,125 @@ | |||
| <template> | |||
| <transition name="fade"> | |||
| <div v-show="show" class="alert-box-wrapper"> | |||
| <div class="alert-box"> | |||
| <div class="alert-box-header"> | |||
| <div class="alert-box-title">{{ title }}</div> | |||
| <div class="alert-box-headerbtn" @click="handleAction('close')">X</div> | |||
| </div> | |||
| <div class="alert-box-content"> | |||
| <div class="alert-box-container">{{ message }}</div> | |||
| </div> | |||
| <div class="alert-box-btns"> | |||
| <button class="cancel-btn" @click="handleAction('cancel')">{{ cancelText }}</button> | |||
| <button class="confirm-btn" @click="handleAction('confirm')">{{ confirmText }}</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </transition> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: "Alert", | |||
| data() { | |||
| return { | |||
| title: "标题", | |||
| message: "这是一段提示内容", | |||
| show: false, | |||
| callback: null, | |||
| cancelText: "取消", | |||
| confirmText: "确定" | |||
| }; | |||
| }, | |||
| methods: { | |||
| handleAction(action) { | |||
| this.callback(action); | |||
| this.destroyVm(); | |||
| }, | |||
| destroyVm() { | |||
| // 销毁 | |||
| this.show = false; | |||
| setTimeout(() => { | |||
| this.$destroy(true); | |||
| this.$el && this.$el.parentNode.removeChild(this.$el); | |||
| }, 500); | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style> | |||
| .fade-enter-active, | |||
| .fade-leave-active { | |||
| transition: opacity 0.3s; | |||
| } | |||
| .fade-enter, | |||
| .fade-leave-to { | |||
| opacity: 0; | |||
| } | |||
| .alert-box-wrapper { | |||
| position: fixed; | |||
| top: 0; | |||
| bottom: 0; | |||
| left: 0; | |||
| right: 0; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| background: rgba(0, 0, 0, 0.5); | |||
| .alert-box { | |||
| display: inline-block; | |||
| width: 92vw; | |||
| padding-bottom: 10px; | |||
| background-color: #fff; | |||
| border-radius: 4px; | |||
| border: 1px solid #303133; | |||
| font-size: 16px; | |||
| text-align: left; | |||
| overflow: hidden; | |||
| .alert-box-header { | |||
| position: relative; | |||
| padding: 15px; | |||
| padding-bottom: 10px; | |||
| .alert-box-title { | |||
| color: #303133; | |||
| } | |||
| .alert-box-headerbtn { | |||
| position: absolute; | |||
| top: 15px; | |||
| right: 15px; | |||
| cursor: pointer; | |||
| color: #909399; | |||
| } | |||
| } | |||
| .alert-box-content { | |||
| padding: 10px 15px; | |||
| color: #606266; | |||
| font-size: 14px; | |||
| } | |||
| .alert-box-btns { | |||
| padding: 5px 15px 0; | |||
| text-align: right; | |||
| .cancel-btn { | |||
| padding: 5px 15px; | |||
| background: #fff; | |||
| border: 1px solid #dcdfe6; | |||
| border-radius: 4px; | |||
| outline: none; | |||
| cursor: pointer; | |||
| } | |||
| .confirm-btn { | |||
| margin-left: 6px; | |||
| padding: 5px 15px; | |||
| color: #fff; | |||
| background-color: #409eff; | |||
| border: 1px solid #409eff; | |||
| border-radius: 4px; | |||
| outline: none; | |||
| cursor: pointer; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,685 @@ | |||
| <template> | |||
| <div :v-model="testing"> | |||
| <div class="api-layout m-button"> | |||
| <input | |||
| type="button" | |||
| value="一键自测" | |||
| class="input-text input-button" | |||
| style="margin: 0; width: 100%; text-align: center;" | |||
| @click="testAllInterface(toast)" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceShowToast'])"> | |||
| <input v-model="toast" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="showToast" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceShowToast()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceShowLongToast'])"> | |||
| <input v-model="longToast" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="longToast" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceShowLongToast()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="iPlayTts ? 'api-layout' : getValidClass(allInterfaces['interfacePlayTts'])"> | |||
| <input v-model="tts" type="text" class="input-text" /> | |||
| <input type="button" value="playTts" class="input-text input-button" @click="interfacePlayTts()" /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceUpdateBrowserLocation'])"> | |||
| <input v-model="bOffset" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="browser" | |||
| class="input-text input-button" | |||
| @click="interfaceUpdateBrowserLocation()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceUpdateCharacterLocation'])"> | |||
| <input v-model="cLocation" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="location" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceUpdateCharacterLocation()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceUpdateCharacterRotation'])"> | |||
| <input v-model="cRotation" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="rotation" | |||
| class="input-text input-button" | |||
| @click="interfaceUpdateCharacterRotation()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceUpdateWallLocation'])"> | |||
| <input v-model="bgLocation" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="WallLocation" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceUpdateWallLocation()" | |||
| /> | |||
| </div> | |||
| <div :class="getValidClass(allInterfaces['interfacePlayMotion'])"> | |||
| <label class="input-text select_wrapper"> | |||
| <select v-model="motion" class="input-text selection"> | |||
| <option>idle</option> | |||
| <option>nod</option> | |||
| <option>shakeHead</option> | |||
| <option>bow</option> | |||
| <option>handWave</option> | |||
| <option>showLeftHand</option> | |||
| <option>showLeftHandTop</option> | |||
| <option>showLeftHandBottom</option> | |||
| <option>showRightHand</option> | |||
| <option>showRightHandTop</option> | |||
| <option>showRightHandBottom</option> | |||
| <option>showBothHandBottom</option> | |||
| </select> | |||
| </label> | |||
| <input | |||
| type="button" | |||
| value="playMotion" | |||
| class="input-text input-button m-button" | |||
| @click="interfacePlayMotion()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceSetBgWallColor']) + ' input-color-layout'"> | |||
| <div class="input-text"> | |||
| <span class="input-color-text">{{ wall }}</span> | |||
| </div> | |||
| <input | |||
| id="color" | |||
| type="color" | |||
| name="color" | |||
| value="#00f4ff" | |||
| class="input-text input-color-button m-button" | |||
| @change="interfaceSetBgWallColor()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceSetBgWallRes'])"> | |||
| <label class="input-text select_wrapper"> | |||
| <select v-model="wallRes" class="input-text selection"> | |||
| <option>3DRoom</option> | |||
| <option>SolidColor</option> | |||
| <option>Landscape</option> | |||
| </select> | |||
| </label> | |||
| <input | |||
| type="button" | |||
| value="setMapRes" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceSetBgWallRes()" | |||
| /> | |||
| </div> | |||
| <div | |||
| v-if="false" | |||
| :class="iGetRobotConfig ? 'api-layout' : getValidClass(allInterfaces['interfaceGetDeviceId'])" | |||
| > | |||
| <input v-model="deviceId" type="text" class="input-text" /> | |||
| <input type="button" value="DeviceId" class="input-text input-button" @click="interfaceGetDeviceId()" /> | |||
| </div> | |||
| <div v-if="false" :class="iAppConfig ? 'api-layout' : getValidClass(allInterfaces['interfaceGetAppConfig'])"> | |||
| <input v-model="appConfig" type="text" class="input-text" /> | |||
| <input type="button" value="AppConfig" class="input-text input-button" @click="interfaceGetAppConfig()" /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceSetCharacter'])"> | |||
| <label class="input-text select_wrapper"> | |||
| <select v-model="character" class="input-text selection"> | |||
| <option>SweetGirl</option> | |||
| <option>BusinessGirl</option> | |||
| <option>AncientGirl</option> | |||
| </select> | |||
| </label> | |||
| <input | |||
| type="button" | |||
| value="setCharacter" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceSetCharacter()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceSetClothes'])"> | |||
| <label class="input-text select_wrapper"> | |||
| <select v-model="clothes" class="input-text selection"> | |||
| <option v-for="c in allClothes" :key="c">{{ c }}</option> | |||
| </select> | |||
| </label> | |||
| <input | |||
| type="button" | |||
| value="setClothes" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceSetClothes()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfacePostMediaEvent'])"> | |||
| <label class="input-text select_wrapper"> | |||
| <select v-model="mediaEvent" class="input-text selection"> | |||
| <option>playing</option> | |||
| <option>pause</option> | |||
| <option>ended</option> | |||
| </select> | |||
| </label> | |||
| <input | |||
| type="button" | |||
| value="postMediaEvent" | |||
| class="input-text input-button m-button" | |||
| @click="interfacePostMediaEvent()" | |||
| /> | |||
| </div> | |||
| <div | |||
| v-if="false" | |||
| :class="iAsrWakeUp ? 'api-layout' : getValidClass(allInterfaces['interfaceSetAsrWakeUpEnable'])" | |||
| > | |||
| <input v-model="asrWakeUp" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="Asr Wake Up" | |||
| class="input-text input-button" | |||
| @click="interfaceSetAsrWakeUpEnable()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceStopPlayTts'])"> | |||
| <input disabled type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="StopPlayTts" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceStopPlayTts()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceEnterImmerseMode'])"> | |||
| <input v-model="immerseMode" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="immerseMode" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceEnterImmerseMode()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceStartVideoTalk'])"> | |||
| <input | |||
| type="button" | |||
| value="StartVideoTalk" | |||
| class="input-text" | |||
| style="width: 61vw;" | |||
| @click="interfaceStartVideoTalk()" | |||
| /> | |||
| <input | |||
| type="button" | |||
| value="StopVideoTalk" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceStopVideoTalk()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceSetVolume'])"> | |||
| <input v-model="setValueText" type="text" class="input-text" /> | |||
| <input type="button" value="setVolume" class="input-text input-button" @click="interfaceSetVolume()" /> | |||
| </div> | |||
| <div v-if="false" :class="iGetVolume ? 'api-layout' : getValidClass(allInterfaces['interfaceGetVolume'])"> | |||
| <input v-model="getValueText" type="text" class="input-text" /> | |||
| <input type="button" value="getVolume" class="input-text input-button" @click="interfaceGetVolume()" /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceDownloadFiles'])"> | |||
| <input disabled type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="downloadFiles" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceDownloadFiles()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" :class="getValidClass(allInterfaces['interfaceTextTriggerNlp'])"> | |||
| <input v-model="ttsText" type="text" class="input-text" /> | |||
| <input | |||
| type="button" | |||
| value="TriggerNlp" | |||
| class="input-text input-button m-button" | |||
| @click="interfaceTextTriggerNlp()" | |||
| /> | |||
| </div> | |||
| <div v-if="false" class="api-layout"> | |||
| <input disabled type="text" class="input-text" /> | |||
| <input type="button" value="LoadAd" class="input-text input-button" @click="loadAd()" /> | |||
| </div> | |||
| <div class="api-layout"> | |||
| <p>cloudminds</p> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| var timer; | |||
| export default { | |||
| name: "DemoView", | |||
| components: {}, | |||
| data() { | |||
| return { | |||
| toast: "A Toast", | |||
| longToast: "A Long Toast", | |||
| tts: "1234567890", | |||
| bOffset: "0, 0, 0, 0", | |||
| cLocation: "-378.8,-0.7,448.3,false", | |||
| cRotation: "0,179.5,0,false", | |||
| motion: "bow", | |||
| wall: "Change BG Color", | |||
| wallRes: "3DRoom", | |||
| exp: "smile, 1", | |||
| mediaEvent: "playing", | |||
| asrWakeUp: true, | |||
| walkGreetEnable: true, | |||
| cameraMode: "level", | |||
| bgLocation: "1400,0,0", | |||
| setValueText: 12, | |||
| getValueText: 0, | |||
| ttsText: "成都天气", | |||
| deviceId: 0, | |||
| testing: true, | |||
| iPlayTts: true, | |||
| iAsrWakeUp: true, | |||
| iGetRobotConfig: true, | |||
| iAppConfig: true, | |||
| iGetVolume: true, | |||
| allInterfaces: new Map(), | |||
| cloudiaEvents: new Map(), | |||
| appConfig: "", | |||
| character: "SweetGirl", | |||
| allClothes: [], | |||
| clothes: "suit_blue", | |||
| immerseMode: false, | |||
| asrEnabled: true | |||
| }; | |||
| }, | |||
| computed: {}, | |||
| watch: {}, | |||
| mounted() { | |||
| let that = this; | |||
| if (that.$parent.tabIndex == 1) { | |||
| let getAllInterfaces = (obj) => | |||
| Object.getOwnPropertyNames(obj).filter((item) => { | |||
| if (typeof obj[item] === "function" && item.startsWith("interface")) { | |||
| that.allInterfaces.set(item, 0); | |||
| console.info(item); | |||
| return item; | |||
| } | |||
| }); | |||
| getAllInterfaces(this); | |||
| } | |||
| }, | |||
| methods: { | |||
| getValidClass(cond) { | |||
| if (cond === undefined || cond === 0) { | |||
| return "api-layout interface-init"; | |||
| } else if (cond === 1) { | |||
| return "api-layout interface-success"; | |||
| } else { | |||
| return "api-layout interface-fail"; | |||
| } | |||
| }, | |||
| interfaceShowToast() { | |||
| let result = window.Cloudia.showToast(this.toast); | |||
| this.allInterfaces["interfaceShowToast"] = result ? 1 : 2; | |||
| }, | |||
| interfaceShowLongToast() { | |||
| let result = window.Cloudia.showLongToast(this.longToast); | |||
| this.allInterfaces["interfaceShowLongToast"] = result ? 1 : 2; | |||
| }, | |||
| interfacePlayTts() { | |||
| let result = window.Cloudia.playTts(this.tts); | |||
| if (result) { | |||
| this.iPlayTts = true; | |||
| timer && window.clearTimeout(timer); | |||
| timer = setTimeout(() => { | |||
| this.iPlayTts = false; | |||
| this.allInterfaces["interfacePlayTts"] = this.cloudiaEvents["ttsPlay"] === 10 ? 1 : 2; | |||
| }, 1500); | |||
| } else { | |||
| this.allInterfaces["interfacePlayTts"] = 2; | |||
| } | |||
| }, | |||
| interfaceUpdateBrowserLocation() { | |||
| this.bOffset = this.bOffset.replace(/ /g, ""); | |||
| let offset = this.bOffset.split(","); | |||
| let result = window.Cloudia.updateBrowserLocation(offset[0], offset[1], offset[2], offset[3]); | |||
| this.allInterfaces["interfaceUpdateBrowserLocation"] = result ? 1 : 2; | |||
| }, | |||
| interfaceUpdateCharacterLocation() { | |||
| this.cLocation = this.cLocation.replace(/ /g, ""); | |||
| let location = this.cLocation.split(","); | |||
| let result = window.Cloudia.updateCharacterLocation(location[0], location[1], location[2], location[3]); | |||
| this.allInterfaces["interfaceUpdateCharacterLocation"] = result ? 1 : 2; | |||
| }, | |||
| interfaceUpdateCharacterRotation() { | |||
| this.cRotation = this.cRotation.replace(/ /g, ""); | |||
| let rotation = this.cRotation.split(","); | |||
| let result = window.Cloudia.updateCharacterRotation(rotation[0], rotation[1], rotation[2], rotation[3]); | |||
| this.allInterfaces["interfaceUpdateCharacterRotation"] = result ? 1 : 2; | |||
| }, | |||
| interfaceUpdateWallLocation() { | |||
| let result = window.Cloudia.setWallLocation(this.bgLocation); | |||
| this.allInterfaces["interfaceUpdateWallLocation"] = result ? 1 : 2; | |||
| }, | |||
| interfacePlayMotion() { | |||
| this.motion = this.motion.replace(/ /g, ""); | |||
| let params = this.motion.split(","); | |||
| let result; | |||
| if (params.length === 2) { | |||
| result = window.Cloudia.playMotion(params[0], params[1]); | |||
| } else { | |||
| result = window.Cloudia.playMotion(params[0]); | |||
| } | |||
| this.allInterfaces["interfacePlayMotion"] = result ? 1 : 2; | |||
| }, | |||
| interfaceSetBgWallColor() { | |||
| //通过使用 getElementById() 来访问 <color> 元素 | |||
| let colorEl = document.getElementById("color"); | |||
| console.info("selected color:" + colorEl.value); | |||
| let color = colorEl.value.replace("#", ""); | |||
| let r = parseInt(color.substring(0, 2), 16) / 255; | |||
| let g = parseInt(color.substring(2, 4), 16) / 255; | |||
| let b = parseInt(color.substring(4, 6), 16) / 255; | |||
| // console.info("Selected color: rgb(" + r + ", " + g + ", " + b + ")"); | |||
| let result = window.Cloudia.setBgWallColor(r + ", " + g + ", " + b); | |||
| this.allInterfaces["interfaceSetBgWallColor"] = result ? 1 : 2; | |||
| }, | |||
| interfaceSetBgWallRes() { | |||
| let result = window.Cloudia.setBgWallResource(this.wallRes); | |||
| this.allInterfaces["interfaceSetBgWallRes"] = result ? 1 : 2; | |||
| }, | |||
| interfaceGetDeviceId() { | |||
| let that = this; | |||
| this.iGetRobotConfig = true; | |||
| return window.Cloudia.getRobotInfoConfig("RobotId").then(function(ReturnValue) { | |||
| if (ReturnValue !== undefined && ReturnValue.ReturnValue !== undefined) { | |||
| that.deviceId = ReturnValue.ReturnValue; | |||
| } | |||
| that.allInterfaces["interfaceGetDeviceId"] = that.deviceId.length > 0 ? 1 : 2; | |||
| that.iGetRobotConfig = false; | |||
| }); | |||
| }, | |||
| interfaceGetAppConfig() { | |||
| let that = this; | |||
| this.iAppConfig = true; | |||
| return window.Cloudia.getAppConfig().then(function(ReturnValue) { | |||
| if (ReturnValue !== undefined && ReturnValue.ReturnValue !== undefined) { | |||
| that.appConfig = ReturnValue.ReturnValue; | |||
| } | |||
| that.allInterfaces["interfaceGetAppConfig"] = that.appConfig.length > 0 ? 1 : 2; | |||
| that.iAppConfig = false; | |||
| }); | |||
| }, | |||
| interfaceSetCharacter() { | |||
| let result = window.Cloudia.setCharacter(this.character); | |||
| this.allClothes = cloudiaConfig.allClothes; | |||
| this.clothes = this.allClothes[0]; | |||
| this.allInterfaces["interfaceSetCharacter"] = result ? 1 : 2; | |||
| return result; | |||
| }, | |||
| interfaceSetClothes() { | |||
| let result = window.Cloudia.setClothes(this.clothes); | |||
| this.allInterfaces["interfaceSetClothes"] = result ? 1 : 2; | |||
| }, | |||
| interfacePostMediaEvent() { | |||
| let result = window.Cloudia.postMediaEvent(this.mediaEvent); | |||
| this.allInterfaces["interfacePostMediaEvent"] = result ? 1 : 2; | |||
| }, | |||
| interfaceSetAsrWakeUpEnable() { | |||
| let result = window.Cloudia.setAsrWakeUpEnable(this.asrWakeUp); | |||
| if (result) { | |||
| this.iAsrWakeUp = true; | |||
| timer && window.clearTimeout(timer); | |||
| timer = setTimeout(() => { | |||
| if (this.cloudiaEvents["asrWakeUp"] === this.asrWakeUp ? 10 : 20) { | |||
| this.allInterfaces["interfaceSetAsrWakeUpEnable"] = 1; | |||
| } else { | |||
| this.allInterfaces["interfaceSetAsrWakeUpEnable"] = 2; | |||
| } | |||
| this.iAsrWakeUp = false; | |||
| }, 1000); | |||
| } else { | |||
| this.allInterfaces["interfaceSetAsrWakeUpEnable"] = 2; | |||
| } | |||
| }, | |||
| interfaceStopPlayTts() { | |||
| let result = window.Cloudia.stopPlayTts(false); | |||
| this.allInterfaces["interfaceStopPlayTts"] = result ? 1 : 2; | |||
| }, | |||
| interfaceEnterImmerseMode() { | |||
| let result = window.Cloudia.enterImmerseMode(this.immerseMode); | |||
| this.allInterfaces["interfaceEnterImmerseMode"] = result ? 1 : 2; | |||
| }, | |||
| interfaceStartVideoTalk() { | |||
| let result = window.Cloudia.startVideoTalk(); | |||
| this.allInterfaces["interfaceStartVideoTalk"] = result ? 1 : 2; | |||
| }, | |||
| interfaceStopVideoTalk() { | |||
| let result = window.Cloudia.stopVideoTalk(); | |||
| this.allInterfaces["interfaceStopVideoTalk"] = result ? 1 : 2; | |||
| }, | |||
| interfaceSetVolume() { | |||
| let result = window.Cloudia.setVolume(this.setValueText); | |||
| this.allInterfaces["interfaceSetVolume"] = result ? 1 : 2; | |||
| }, | |||
| interfaceGetVolume() { | |||
| this.iGetVolume = true; | |||
| return window.Cloudia.getVolume().then((ReturnValue) => { | |||
| if (ReturnValue !== undefined && ReturnValue.ReturnValue !== undefined) { | |||
| this.getValueText = ReturnValue.ReturnValue; | |||
| console.info("========:" + this.getValueText + ", " + this.setValueText); | |||
| this.allInterfaces["interfaceGetVolume"] = | |||
| this.getValueText.toString() === this.setValueText.toString() ? 1 : 2; | |||
| } else { | |||
| this.allInterfaces["interfaceGetVolume"] = 2; | |||
| } | |||
| this.iGetVolume = false; | |||
| }); | |||
| }, | |||
| interfaceDownloadFiles() { | |||
| let result = window.Cloudia.downloadFiles( | |||
| "test", | |||
| JSON.stringify([ | |||
| { | |||
| name: "04070c61b7d40b5a90b98640645493cb.jpeg", | |||
| url: "https://127.0.0.1" | |||
| }, | |||
| { | |||
| name: "112350h4wcwa94c9ch3oth.jpg", | |||
| url: "https://127.0.0.1" | |||
| } | |||
| ]) | |||
| ); | |||
| this.allInterfaces["interfaceDownloadFiles"] = result ? 1 : 2; | |||
| }, | |||
| interfaceTextTriggerNlp() { | |||
| let result = window.Cloudia.textTriggerNlp(this.ttsText); | |||
| this.allInterfaces["interfaceTextTriggerNlp"] = result ? 1 : 2; | |||
| }, | |||
| getRandomInt(min, max) { | |||
| min = Math.ceil(min); | |||
| max = Math.floor(max); | |||
| return Math.floor(Math.random() * (max - min)) + min; //不含最大值,含最小值 | |||
| }, | |||
| loadAd() { | |||
| // TODO !!!!不同的环境需要切换不同的baseUrl!!!! | |||
| let baseUrl = "https://127.0.0.1"; | |||
| let timestamp = new Date().getTime(); | |||
| // TODO 填写你自己的appId | |||
| let appId = Config.appId; | |||
| let nonceStr = this.getRandomInt(1000, 9999); | |||
| // TODO 填写你自己的appSecret | |||
| let appSecret = Config.appSecret; | |||
| let sign = md5("appId" + appId + "timestamp" + timestamp + "nonceStr" + nonceStr + appSecret); | |||
| // console.info("appId:" + appId); | |||
| // console.info("appSecret:" + appSecret); | |||
| // console.info("sign:" + sign); | |||
| const headers = { | |||
| "Content-Type": "application/json", | |||
| appId: appId, | |||
| timestamp: timestamp, | |||
| nonceStr: nonceStr, | |||
| sign: sign | |||
| }; | |||
| let url = "/crss-robot/ads/robotapi/v1/adSpace/get?pageCode=home&robotCode=" + this.deviceId; | |||
| fetch(baseUrl + url, { headers }) | |||
| .then(async (response) => { | |||
| const data = await response.json(); | |||
| if (!response.ok) { | |||
| // get error message from body or default to response statusText | |||
| const error = (data && data.message) || response.statusText; | |||
| return Promise.reject(error); | |||
| } | |||
| console.info("load ad data success:" + JSON.stringify(data)); | |||
| if (data.code === 0 && data.data && data.data.adList) { | |||
| // 拼接完整URL | |||
| data.data.adList.map((e) => { | |||
| e.url = baseUrl + e.url; | |||
| }); | |||
| let adData = JSON.stringify(data.data.adList); | |||
| // 下载广告数据,下载成功之后会在event事件中收到通知 | |||
| window.Cloudia.downloadFiles("home", adData); | |||
| console.info("ad data:" + adData); | |||
| } else { | |||
| console.warn("ad data is empty"); | |||
| } | |||
| }) | |||
| .catch((error) => { | |||
| console.error("load ad data error:" + error); | |||
| }); | |||
| }, | |||
| insertNlp() { | |||
| this.addMsgList(2, "测试一下,测试一下"); | |||
| if (this.nplList.length > 10) { | |||
| this.nplList.splice(10, this.nplList.length - 10); | |||
| } | |||
| this.nplList = [ | |||
| { question_id: new Date().getTime(), question_text: "test test", answer: "answer....." }, | |||
| ...this.nplList | |||
| ]; | |||
| this.$axios | |||
| .post(this.$cmdList.serverDb + "/api/db/save", { | |||
| id: new Date().getTime(), | |||
| text: "test test", | |||
| answer: "answer....." | |||
| }) | |||
| .then((res) => { | |||
| console.log(res); | |||
| }); | |||
| }, | |||
| testAllInterface() { | |||
| let i = 0; | |||
| for (let [key, value] of this.allInterfaces) { | |||
| timer && window.clearTimeout(timer); | |||
| timer = setTimeout(() => { | |||
| this.testing = true; | |||
| try { | |||
| this[key](); | |||
| } catch (e) { | |||
| this.allInterfaces[key] = 2; | |||
| console.error("interface exec failed:" + key + ", " + value + "----" + e); | |||
| } | |||
| this.testing = false; | |||
| }, ++i * 1000); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style> | |||
| .log { | |||
| height: 100%; | |||
| text-align: left; | |||
| overflow-y: scroll; | |||
| margin-right: 0; | |||
| padding-right: 0; | |||
| } | |||
| .cmd { | |||
| margin-left: 2vw; | |||
| color: #803300; | |||
| font-size: 4vh; | |||
| display: inline-block; | |||
| } | |||
| .api-layout { | |||
| margin-bottom: 0.2vh; | |||
| justify-content: left; | |||
| width: 30%; | |||
| text-align: left; | |||
| } | |||
| .input-text { | |||
| height: 2vh; | |||
| line-height: 2vh; | |||
| font-size: 1vh; | |||
| color: black; | |||
| text-align: left; | |||
| width: 20vw; | |||
| background-color: rgba(255, 255, 255, 0.4); | |||
| border: none; | |||
| padding-left: 1vw; | |||
| } | |||
| .select_wrapper { | |||
| padding: 0; | |||
| background-color: transparent; | |||
| } | |||
| .selection { | |||
| padding: 0; | |||
| width: 61vw; | |||
| background-color: rgba(255, 255, 255, 0.4); | |||
| } | |||
| .input-button { | |||
| width: 34vw; | |||
| margin-left: 2vw; | |||
| } | |||
| .m-button { | |||
| background-color: rgba(59, 140, 207, 0.5); | |||
| } | |||
| .input-color-layout { | |||
| display: -webkit-box; | |||
| } | |||
| .input-color-text { | |||
| font: 400 3vh Arial; | |||
| text-align: left; | |||
| display: contents; | |||
| } | |||
| .input-color-button { | |||
| padding: 0; | |||
| margin-left: 2vw; | |||
| width: 34vw; | |||
| display: flex; | |||
| } | |||
| .floats { | |||
| position: absolute; | |||
| right: 1vw; | |||
| width: 48vw; | |||
| text-align: right; | |||
| } | |||
| .interface-init { | |||
| background-color: transparent; | |||
| } | |||
| .interface-success { | |||
| background-color: #57b73b; | |||
| } | |||
| .interface-fail { | |||
| background-color: #803300; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,98 @@ | |||
| <template> | |||
| <div v-if="showMeetingAppoint" class="root" @click="close($event)"> | |||
| <div ref="content" class="content"> | |||
| <div class="appoint-info">预约成功</div> | |||
| <div class="oppoint-time-div"> | |||
| <div class="appoint-info">您已完成会议预约</div> | |||
| <div class="appoint-info">主题:{{ topic }}</div> | |||
| <div class="appoint-info">会议室:{{ meetingRoom }}</div> | |||
| <div v-for="(time, index) in timeList" :key="index" class="appoint-time-item"> | |||
| {{ time }} | |||
| </div> | |||
| </div> | |||
| <div class="appoint-info">{{ timeCount }}秒后自动关闭</div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import { mapMutations } from "vuex"; | |||
| export default { | |||
| props: ["showMeetingAppoint", "topic", "meetingRoom", "times"], | |||
| data() { | |||
| return { | |||
| timeList: [], | |||
| timeCount: 3 | |||
| }; | |||
| }, | |||
| mounted() {}, | |||
| methods: { | |||
| ...mapMutations(["setLastAction"]), | |||
| close(ev) { | |||
| if (this.$refs.content && !this.$refs.content.contains(ev.target)) { | |||
| this.$emit("showMeetingAppointResult"); | |||
| } | |||
| }, | |||
| quit() { | |||
| this.$emit("showMeetingAppointResult"); | |||
| } | |||
| }, | |||
| watch: { | |||
| times(newVal, oldVal) { | |||
| //console.log(newVal) | |||
| //console.log(JSON.stringify(newVal)) | |||
| this.timeList = newVal; | |||
| }, | |||
| showMeetingAppoint(newVal, oldVal) { | |||
| if (newVal) { | |||
| this.setLastAction("meetingOk"); | |||
| let self = this; | |||
| var timeId = setInterval(function() { | |||
| self.timeCount -= 1; | |||
| if (self.timeCount == 0) { | |||
| clearInterval(timeId); | |||
| self.quit(); | |||
| } | |||
| }, 1000); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .root { | |||
| position: fixed; | |||
| top: 0; | |||
| left: 0; | |||
| background: transparent; | |||
| width: 100vw; | |||
| height: 100vh; | |||
| z-index: 2; | |||
| } | |||
| .content { | |||
| position: absolute; | |||
| left: 5%; | |||
| top: 20%; | |||
| /*transform: translate(-50%, -50%);*/ | |||
| z-index: 10; | |||
| width: 90%; | |||
| padding: 10px; | |||
| border-radius: 5px; | |||
| overflow: hidden; | |||
| background: white; | |||
| } | |||
| .appoint-info { | |||
| padding: 10px; | |||
| color: black; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| .appoint-time-item { | |||
| padding: 3px; | |||
| color: black; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| .oppoint-time-div { | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,94 @@ | |||
| <template> | |||
| <div v-if="showMeetingCancel" class="root" @click="close($event)"> | |||
| <div ref="content" class="content"> | |||
| <div class="appoint-info">已取消预约</div> | |||
| <div class="oppoint-time-div"> | |||
| <div class="appoint-info">会议预约详情</div> | |||
| <div class="appoint-info">主题:{{ order.meetingTitle }}</div> | |||
| <div class="appoint-info">会议室:{{ order.roomName }}</div> | |||
| <div class="appoint-info">{{ order.orderDate }},{{ order.orderTime }}至{{ order.endTime }}</div> | |||
| </div> | |||
| <div class="appoint-info">{{ timeCount }}秒后自动关闭</div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import { mapMutations } from "vuex"; | |||
| export default { | |||
| props: ["showMeetingCancel", "order"], | |||
| data() { | |||
| return { | |||
| timeList: [], | |||
| timeCount: 3, | |||
| curOrder: {} | |||
| }; | |||
| }, | |||
| watch: { | |||
| order(newVal, oldVal) { | |||
| //console.log(newVal) | |||
| //console.log(JSON.stringify(newVal)) | |||
| this.setLastAction("meetingCancel"); | |||
| this.curOrder = newVal; | |||
| let self = this; | |||
| var timeId = setInterval(function() { | |||
| self.timeCount -= 1; | |||
| if (self.timeCount == 0) { | |||
| clearInterval(timeId); | |||
| self.quit(); | |||
| self.timeCount = 3; | |||
| } | |||
| }, 1000); | |||
| } | |||
| }, | |||
| mounted() {}, | |||
| methods: { | |||
| ...mapMutations(["setLastAction"]), | |||
| close(ev) { | |||
| if (this.$refs.content && !this.$refs.content.contains(ev.target)) { | |||
| this.$emit("showMeetingCancelResult"); | |||
| } | |||
| }, | |||
| quit() { | |||
| this.$emit("showMeetingCancelResult"); | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .root { | |||
| position: fixed; | |||
| top: 0; | |||
| left: 0; | |||
| background: #efefef; | |||
| width: 100vw; | |||
| height: 100vh; | |||
| z-index: 2; | |||
| } | |||
| .content { | |||
| position: absolute; | |||
| left: 5%; | |||
| top: 30%; | |||
| /*transform: translate(-50%, -50%);*/ | |||
| z-index: 10; | |||
| width: 90%; | |||
| padding: 10px; | |||
| border-radius: 5px; | |||
| overflow: hidden; | |||
| background: white; | |||
| } | |||
| .appoint-info { | |||
| padding: 10px; | |||
| color: black; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| .appoint-time-item { | |||
| padding: 3px; | |||
| color: black; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| .oppoint-time-div { | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,69 @@ | |||
| <template> | |||
| <div v-if="showMeetingRoom" class="root" @click="close($event)"> | |||
| <div class="content" ref="content" :styles="{top: contentTop + 'px', left: 0}"> | |||
| <div v-for="(room, index) in rooms" | |||
| class="room-item" | |||
| :key="index" | |||
| @click="selectRoom(room)"> | |||
| {{room.name}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| props: ["rooms", "top", "showMeetingRoom"], | |||
| data(){ | |||
| return{ | |||
| contentTop: 0, | |||
| } | |||
| }, | |||
| created() { | |||
| this.contentTop = this.top | |||
| console.log(); | |||
| }, | |||
| methods: { | |||
| close(ev) { | |||
| if(this.$refs.content && !this.$refs.content.contains(ev.target)){ | |||
| let data = {room:null} | |||
| this.$emit("showRooms", data); | |||
| } | |||
| }, | |||
| selectRoom(room){ | |||
| let data = {room:room} | |||
| this.$emit("showRooms", data); | |||
| } | |||
| }, | |||
| watch: { | |||
| top(newVal, oldVal) { | |||
| console.log('-----' + newVal); | |||
| this.popupTop = newVal | |||
| }, | |||
| }, | |||
| } | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .root{ | |||
| position: fixed; | |||
| top: 0; | |||
| left: 0; | |||
| background: transparent; | |||
| width: 100vw; | |||
| height: 100vh; | |||
| z-index: 2; | |||
| } | |||
| .content{ | |||
| position: absolute; | |||
| /*transform: translate(-50%, -50%);*/ | |||
| z-index: 10; | |||
| left: 0; | |||
| top: 40%; | |||
| width: 100%; | |||
| border-radius: 5px; | |||
| overflow: hidden; | |||
| } | |||
| .room-item{ | |||
| padding: 10px; | |||
| background: white; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,68 @@ | |||
| <template> | |||
| <div v-if="showMeetingTopic" class="root" @click="close($event)"> | |||
| <div class="content" ref="content"> | |||
| <div v-for="(topic, index) in topics" | |||
| class="topic-item" | |||
| :key="index" | |||
| @click="selectTopic(topic)"> | |||
| {{topic}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| props: ["topics", "top", "showMeetingTopic"], | |||
| data(){ | |||
| return{ | |||
| contentTop: 0, | |||
| } | |||
| }, | |||
| created() { | |||
| this.contentTop = this.top | |||
| console.log(); | |||
| }, | |||
| methods: { | |||
| close(ev) { | |||
| if(this.$refs.content && !this.$refs.content.contains(ev.target)){ | |||
| let data = {topic: null} | |||
| this.$emit("showTopics", data); | |||
| } | |||
| }, | |||
| selectTopic(topic){ | |||
| let data = {topic: topic} | |||
| this.$emit("showTopics", data); | |||
| } | |||
| }, | |||
| watch: { | |||
| top(newVal, oldVal) { | |||
| console.log('-----' + newVal); | |||
| this.contentTop = newVal | |||
| }, | |||
| }, | |||
| } | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .root{ | |||
| position: fixed; | |||
| top: 0; | |||
| left: 0; | |||
| background: transparent; | |||
| width: 100vw; | |||
| height: 100vh; | |||
| z-index: 2; | |||
| } | |||
| .content{ | |||
| position: absolute; | |||
| left: 0; | |||
| top: 40%; | |||
| z-index: 10; | |||
| width: 100%; | |||
| border-radius: 5px; | |||
| overflow: hidden; | |||
| } | |||
| .topic-item{ | |||
| padding: 10px; | |||
| background: white; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,82 @@ | |||
| <template> | |||
| <div v-if="showMeetingTopicDefine" class="root" @click="close($event)"> | |||
| <div ref="content" class="content"> | |||
| <div class="meeting-topic">会议主题</div> | |||
| <input v-model="topic" class="topic_input" type="text" placeholder="请输入会议主题" /> | |||
| <div class="confirm-div"> | |||
| <div class="meeting-topic-confirm" @click="confirm">确定</div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| props: ["showMeetingTopicDefine"], | |||
| data() { | |||
| return { | |||
| topic: "" | |||
| }; | |||
| }, | |||
| methods: { | |||
| close(ev) { | |||
| if (this.$refs.content && !this.$refs.content.contains(ev.target)) { | |||
| this.$emit("showTopicsDefine", null); | |||
| } | |||
| }, | |||
| confirm() { | |||
| console.log(this.topic); | |||
| this.$emit("showTopicsDefine", this.topic); | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .root { | |||
| position: fixed; | |||
| top: 0; | |||
| left: 0; | |||
| background: transparent; | |||
| width: 100vw; | |||
| height: 100vh; | |||
| z-index: 2; | |||
| } | |||
| .content { | |||
| position: absolute; | |||
| left: 5%; | |||
| top: 40%; | |||
| /*transform: translate(-50%, -50%);*/ | |||
| z-index: 10; | |||
| width: 90%; | |||
| padding: 10px; | |||
| border-radius: 5px; | |||
| overflow: hidden; | |||
| background: white; | |||
| } | |||
| .meeting-topic { | |||
| padding: 10px; | |||
| color: black; | |||
| font-size: 18px; | |||
| text-align: center; | |||
| } | |||
| .topic_input { | |||
| padding: 10px; | |||
| margin-top: 10px; | |||
| } | |||
| .confirm-div { | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| margin-top: 20px; | |||
| } | |||
| .meeting-topic-confirm { | |||
| padding-left: 50px; | |||
| padding-right: 50px; | |||
| padding-top: 10px; | |||
| padding-bottom: 10px; | |||
| color: white; | |||
| font-size: 16px; | |||
| font-weight: bold; | |||
| border-radius: 10px; | |||
| background: #1ca7f5; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,89 @@ | |||
| <template> | |||
| <div v-if="showVisitorAppoint" class="root" @click="close($event)"> | |||
| <div ref="content" class="content"> | |||
| <div class="appoint-info">预约成功!</div> | |||
| <div class="appoint-time-div"> | |||
| <div class="appoint-info">预约访客名称:{{ visitorName }}</div> | |||
| <div class="appoint-info">预约人:{{ appointName }}</div> | |||
| <div class="appoint-info">访客到达后联系手机号:{{ appointPhone }}</div> | |||
| </div> | |||
| <div class="appoint-tip">客人到访登记后会请客人在休息区等您,同时为您发送提醒短信。</div> | |||
| <div class="appoint-info">{{ timeCount }}秒后自动关闭</div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import { mapMutations } from "vuex"; | |||
| export default { | |||
| props: ["showVisitorAppoint", "visitorName", "appointName", "appointPhone"], | |||
| data() { | |||
| return { | |||
| timeCount: 3 | |||
| }; | |||
| }, | |||
| mounted() {}, | |||
| methods: { | |||
| ...mapMutations(["setLastAction"]), | |||
| close(ev) { | |||
| if (this.$refs.content && !this.$refs.content.contains(ev.target)) { | |||
| this.$emit("showVisitorAppointResult"); | |||
| } | |||
| }, | |||
| quit() { | |||
| this.$emit("showVisitorAppointResult"); | |||
| } | |||
| }, | |||
| watch: { | |||
| showVisitorAppoint(newVal, oldVal) { | |||
| if (newVal) { | |||
| let self = this; | |||
| this.setLastAction("visitorAppoint"); | |||
| var timeId = setInterval(function() { | |||
| self.timeCount -= 1; | |||
| if (self.timeCount == 0) { | |||
| clearInterval(timeId); | |||
| self.quit(); | |||
| self.timeCount = 3; | |||
| } | |||
| }, 1000); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .root { | |||
| position: fixed; | |||
| top: 0; | |||
| left: 0; | |||
| background: #00efefef; | |||
| width: 100vw; | |||
| height: 100vh; | |||
| z-index: 2; | |||
| } | |||
| .content { | |||
| position: absolute; | |||
| left: 5%; | |||
| top: 30%; | |||
| /*transform: translate(-50%, -50%);*/ | |||
| z-index: 10; | |||
| width: 90%; | |||
| padding: 10px; | |||
| border-radius: 5px; | |||
| overflow: hidden; | |||
| background: white; | |||
| } | |||
| .appoint-info { | |||
| padding: 10px; | |||
| color: black; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| .appoint-tip { | |||
| padding: 10px; | |||
| color: #efefef; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,85 @@ | |||
| <template> | |||
| <div v-if="showVisitorRegister" class="root" @click="close($event)"> | |||
| <div ref="content" class="content"> | |||
| <div class="register-info">已完成登记</div> | |||
| <div class="register-time-div"> | |||
| <div class="register-info">到访客人:{{ visitorName }}</div> | |||
| </div> | |||
| <div class="register-info">{{ timeCount }}秒后自动关闭</div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import { mapMutations } from "vuex"; | |||
| export default { | |||
| props: ["showVisitorRegister", "visitorName"], | |||
| data() { | |||
| return { | |||
| timeCount: 3 | |||
| }; | |||
| }, | |||
| mounted() {}, | |||
| methods: { | |||
| ...mapMutations(["setLastAction"]), | |||
| close(ev) { | |||
| if (this.$refs.content && !this.$refs.content.contains(ev.target)) { | |||
| this.$emit("showVisitorRegisterResult"); | |||
| } | |||
| }, | |||
| quit() { | |||
| this.$emit("showVisitorRegisterResult"); | |||
| } | |||
| }, | |||
| watch: { | |||
| showVisitorRegister(newVal, oldVal) { | |||
| if (newVal) { | |||
| this.setLastAction("visitorReg"); | |||
| let self = this; | |||
| var timeId = setInterval(function() { | |||
| self.timeCount -= 1; | |||
| if (self.timeCount == 0) { | |||
| clearInterval(timeId); | |||
| self.quit(); | |||
| self.timeCount = 3; | |||
| } | |||
| }, 1000); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .root { | |||
| position: fixed; | |||
| top: 0; | |||
| left: 0; | |||
| background: #00efefef; | |||
| width: 100vw; | |||
| height: 100vh; | |||
| z-index: 2; | |||
| } | |||
| .content { | |||
| position: absolute; | |||
| left: 5%; | |||
| top: 30%; | |||
| /*transform: translate(-50%, -50%);*/ | |||
| z-index: 10; | |||
| width: 90%; | |||
| padding: 10px; | |||
| border-radius: 5px; | |||
| overflow: hidden; | |||
| background: white; | |||
| } | |||
| .register-info { | |||
| padding: 10px; | |||
| color: black; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| .register-tip { | |||
| padding: 10px; | |||
| color: #efefef; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,398 @@ | |||
| <template> | |||
| <div v-if="visible" :class="['json-view-container', theme, `deep-${currentDeep}`]"> | |||
| <div | |||
| :class="['json-view', length ? 'closeable' : '']" | |||
| :style="{ fontSize: fontSize + 'px', lineHeight: lineHeight + 'px' }" | |||
| > | |||
| <!--icon-style-square--> | |||
| <span v-if="length && iconStyle === 'square'" class="angle" @click="toggleClose"> | |||
| <svg | |||
| v-if="innerclosed" | |||
| :fill="iconColors[0]" | |||
| width="1em" | |||
| height="1em" | |||
| viewBox="0 0 1792 1792" | |||
| style="vertical-align: middle; color: rgb(42, 161, 152); height: 1em; width: 1em;" | |||
| > | |||
| <path | |||
| d="M1344 800v64q0 14-9 23t-23 9h-352v352q0 14-9 23t-23 9h-64q-14 0-23-9t-9-23v-352h-352q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h352v-352q0-14 9-23t23-9h64q14 0 23 9t9 23v352h352q14 0 23 9t9 23zm128 448v-832q0-66-47-113t-113-47h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z" | |||
| ></path> | |||
| </svg> | |||
| <svg | |||
| v-if="!innerclosed" | |||
| :fill="iconColors[1]" | |||
| width="1em" | |||
| height="1em" | |||
| viewBox="0 0 1792 1792" | |||
| style="vertical-align: middle; color: rgb(88, 110, 117); height: 1em; width: 1em;" | |||
| > | |||
| <path | |||
| d="M1344 800v64q0 14-9 23t-23 9h-832q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h832q14 0 23 9t9 23zm128 448v-832q0-66-47-113t-113-47h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z" | |||
| ></path> | |||
| </svg> | |||
| </span> | |||
| <!--icon-style-circle--> | |||
| <span v-if="length && iconStyle === 'circle'" class="angle" @click="toggleClose"> | |||
| <svg | |||
| v-if="!innerclosed" | |||
| viewBox="0 0 24 24" | |||
| :fill="iconColors[0]" | |||
| preserveAspectRatio="xMidYMid meet" | |||
| style="vertical-align: middle; color: rgb(1, 160, 228); height: 1em; width: 1em;" | |||
| > | |||
| <path | |||
| d="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M7,13H17V11H7" | |||
| ></path> | |||
| </svg> | |||
| <svg | |||
| v-if="innerclosed" | |||
| viewBox="0 0 24 24" | |||
| :fill="iconColors[1]" | |||
| preserveAspectRatio="xMidYMid meet" | |||
| style="vertical-align: middle; color: rgb(161, 106, 148); height: 1em; width: 1em;" | |||
| > | |||
| <path | |||
| d="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M13,7H11V11H7V13H11V17H13V13H17V11H13V7Z" | |||
| ></path> | |||
| </svg> | |||
| </span> | |||
| <!--icon-style-triangle--> | |||
| <span v-if="length && iconStyle === 'triangle'" class="angle" @click="toggleClose"> | |||
| <svg | |||
| v-if="!innerclosed" | |||
| viewBox="0 0 15 15" | |||
| :fill="iconColors[0]" | |||
| style="vertical-align: top; color: #3c4047; height: 1em; width: 1em; padding-left: 2px;" | |||
| > | |||
| <path d="M0 5l6 6 6-6z"></path> | |||
| </svg> | |||
| <svg | |||
| v-if="innerclosed" | |||
| viewBox="0 0 15 15" | |||
| :fill="iconColors[1]" | |||
| style="vertical-align: top; color: #3c4047; height: 1em; width: 1em; padding-left: 2px;" | |||
| > | |||
| <path d="M0 14l6-6-6-6z"></path> | |||
| </svg> | |||
| </span> | |||
| <div class="content-wrap"> | |||
| <p :class="['first-line', length > 0 ? 'pointer' : '']" @click="toggleClose"> | |||
| <span v-if="jsonKey" class="json-key">"{{ jsonKey }}": </span> | |||
| <span v-if="length" | |||
| >{{ prefix }}{{ innerclosed ? "..." + subfix : "" }} | |||
| <span class="json-note">{{ innerclosed ? length + " items" : "" }}</span> | |||
| </span> | |||
| <span v-if="!length">{{ `${isArray ? "[]" : "{}"}${isLast ? "" : ","}` }}</span> | |||
| </p> | |||
| <div v-if="!innerclosed && length" class="json-body"> | |||
| <template v-for="(item, index) in items"> | |||
| <json-view | |||
| v-if="item.isJSON" | |||
| :key="index" | |||
| :closed="isClose()" | |||
| :data="item.value" | |||
| :json-key="item.key" | |||
| :current-deep="templateDeep + 1" | |||
| :deep="deep" | |||
| :icon-style="iconStyle" | |||
| :theme="theme" | |||
| :font-size="fontSize" | |||
| :line-height="lineHeight" | |||
| :icon-color="iconColors" | |||
| :is-last="index === items.length - 1" | |||
| :has-siblings="item.hasSiblings" | |||
| /> | |||
| <p v-else :key="index" class="json-item"> | |||
| <span class="json-key">{{ isArray ? "" : '"' + item.key + '":' }}</span> | |||
| <span :class="['json-value', getDataType(item.value)]"> | |||
| {{ | |||
| `${getDataType(item.value) === "string" ? '"' : ""}${formatValue(item.value)}${ | |||
| getDataType(item.value) === "string" ? '"' : "" | |||
| }${index === items.length - 1 ? "" : ","}` | |||
| }} | |||
| </span> | |||
| </p> | |||
| </template> | |||
| <span v-if="!innerclosed" class="base-line"></span> | |||
| </div> | |||
| <p v-if="!innerclosed" class="last-line"> | |||
| <span>{{ subfix }}</span> | |||
| </p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: "JsonView", | |||
| props: { | |||
| data: { | |||
| // 传入的json数据 | |||
| type: [Object, Array], | |||
| required: true | |||
| }, | |||
| jsonKey: { | |||
| // json的key值,用于第二层及二层以上的组件的key值 | |||
| type: String, | |||
| default: "" | |||
| }, | |||
| closed: { | |||
| // 是否折叠 | |||
| type: Boolean, | |||
| default: false | |||
| }, | |||
| isLast: { | |||
| //是否是最后一行 | |||
| type: Boolean, | |||
| default: true | |||
| }, | |||
| fontSize: { | |||
| //字体大小 | |||
| type: Number, | |||
| default: 14 | |||
| }, | |||
| lineHeight: { | |||
| //行高 | |||
| type: Number, | |||
| default: 24 | |||
| }, | |||
| deep: { | |||
| // 展开深度 | |||
| type: Number, | |||
| default: 3 | |||
| }, | |||
| currentDeep: { | |||
| // 当前为递归的第几层 | |||
| type: Number, | |||
| default: 1 | |||
| }, | |||
| iconStyle: { | |||
| // 折叠icon样式 | |||
| type: String, | |||
| default: "square" | |||
| }, | |||
| iconColor: { | |||
| //icon颜色 | |||
| type: Array, | |||
| default() { | |||
| return []; | |||
| } | |||
| }, | |||
| theme: { | |||
| // 主题 | |||
| type: String, | |||
| default: "" | |||
| }, | |||
| hasSiblings: { | |||
| // 是否有兄弟节点 | |||
| type: Boolean, | |||
| default: true | |||
| } | |||
| }, | |||
| data() { | |||
| return { | |||
| innerclosed: this.closed, | |||
| templateDeep: this.currentDeep, | |||
| visible: false | |||
| }; | |||
| }, | |||
| computed: { | |||
| isArray() { | |||
| return this.getDataType(this.data) === "array"; | |||
| }, | |||
| length() { | |||
| return this.isArray ? this.data.length : Object.keys(this.data).length; | |||
| }, | |||
| subfix() { | |||
| const data = this.data; | |||
| if (this.isEmptyArrayOrObject(data)) { | |||
| // 如果是空数组或空对象 | |||
| return ""; | |||
| } else { | |||
| return (this.isArray ? "]" : "}") + (this.isLast ? "" : ","); | |||
| } | |||
| }, | |||
| prefix() { | |||
| return this.isArray ? "[" : "{"; | |||
| }, | |||
| items() { | |||
| const json = this.data; | |||
| if (this.isArray) { | |||
| return json.map((item) => { | |||
| const isJSON = this.isObjectOrArray(item); | |||
| return { | |||
| value: item, | |||
| isJSON, | |||
| key: "" | |||
| }; | |||
| }); | |||
| } | |||
| return Object.keys(json).map((key) => { | |||
| const item = json[key]; | |||
| const isJSON = this.isObjectOrArray(item); | |||
| return { | |||
| value: item, | |||
| isJSON, | |||
| key | |||
| }; | |||
| }); | |||
| }, | |||
| iconColors() { | |||
| const { theme, iconColor } = this; | |||
| if (iconColor.length === 2) { | |||
| return iconColor; | |||
| } else { | |||
| if (theme === "one-dark") { | |||
| return ["#747983", "#747983"]; | |||
| } else if (theme === "vs-code") { | |||
| return ["#c6c6c6", "#c6c6c6"]; | |||
| } else { | |||
| return ["#747983", "#747983"]; | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| watch: { | |||
| closed() { | |||
| this.innerclosed = this.closed; | |||
| } | |||
| }, | |||
| mounted() { | |||
| setTimeout(() => { | |||
| this.visible = true; | |||
| }, 0); | |||
| }, | |||
| methods: { | |||
| formatValue(data) { | |||
| if (data && data._isBigNumber) { | |||
| return data.toString(10); | |||
| } | |||
| return data; | |||
| }, | |||
| getDataType(data) { | |||
| return data && data._isBigNumber | |||
| ? "number" | |||
| : Object.prototype.toString | |||
| .call(data) | |||
| .slice(8, -1) | |||
| .toLowerCase(); | |||
| }, | |||
| isObjectOrArray(source) { | |||
| return ["array", "object"].includes(this.getDataType(source)); | |||
| }, | |||
| toggleClose() { | |||
| if (this.length === 0) { | |||
| return; | |||
| } | |||
| if (this.innerclosed) { | |||
| this.innerclosed = false; | |||
| } else { | |||
| this.innerclosed = true; | |||
| } | |||
| }, | |||
| isClose() { | |||
| return this.templateDeep + 1 > this.deep; | |||
| }, | |||
| isEmptyArrayOrObject(data) { | |||
| // 空数组或者空对象 | |||
| return [{}, []].map((item) => JSON.stringify(item)).includes(JSON.stringify(data)); | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .json-view-container { | |||
| background-color: transparent; | |||
| &.deep-1 { | |||
| // overflow: auto; | |||
| padding-right: 10px; | |||
| } | |||
| .json-view { | |||
| position: relative; | |||
| display: block; | |||
| width: 100%; | |||
| height: 100%; | |||
| white-space: nowrap; | |||
| padding-left: 2rem; | |||
| box-sizing: border-box; | |||
| cursor: default; | |||
| .json-note { | |||
| color: #909399; | |||
| font-size: 12px; | |||
| font-style: italic; | |||
| } | |||
| .json-key { | |||
| color: #8c6325; | |||
| } | |||
| .json-value { | |||
| display: inline-block; | |||
| color: #57b73b; | |||
| word-break: break-all; | |||
| white-space: normal; | |||
| &.number { | |||
| color: #2d8cf0; | |||
| } | |||
| &.string { | |||
| color: #57b73b; | |||
| } | |||
| &.boolean { | |||
| color: #eb3324; | |||
| } | |||
| &.null { | |||
| color: #eb3324; | |||
| } | |||
| } | |||
| .json-item { | |||
| margin: 0; | |||
| padding-left: 2rem; | |||
| display: flex; | |||
| } | |||
| .first-line { | |||
| padding: 0; | |||
| margin: 0; | |||
| &.pointer { | |||
| cursor: pointer !important; | |||
| } | |||
| } | |||
| .json-body { | |||
| position: relative; | |||
| padding: 0; | |||
| margin: 0; | |||
| .base-line { | |||
| position: absolute; | |||
| height: 100%; | |||
| border-left: 1px dashed #bbb; | |||
| top: 0; | |||
| left: 2px; | |||
| &:hover { | |||
| } | |||
| } | |||
| } | |||
| .last-line { | |||
| padding: 0; | |||
| margin: 0; | |||
| } | |||
| .angle { | |||
| position: absolute; | |||
| display: block; | |||
| cursor: pointer; | |||
| float: left; | |||
| width: 20px; | |||
| text-align: center; | |||
| /*left: ~"calc(2rem - 18px)";*/ | |||
| left: 12px; | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,193 @@ | |||
| <template> | |||
| <div class="recommendPage"> | |||
| <swiper v-if="initOrNot" ref="videoSwiper" :options="swiperOption"> | |||
| <swiper-slide v-for="(item, index) in mediaNews" :key="index"> | |||
| <video | |||
| v-if="item.type === 1" | |||
| controls | |||
| muted="muted" | |||
| autoplay="autoplay" | |||
| class="multimedia" | |||
| style="width: 100%;object-fit: cover" | |||
| @ended="endVideo(index)" | |||
| > | |||
| <source :src="item.url" type="video/mp4" /> | |||
| </video> | |||
| <img v-else :src="item.url" class="multimedia" style="width: 100%;object-fit: cover" /> | |||
| </swiper-slide> | |||
| </swiper> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import { swiper, swiperSlide } from "vue-awesome-swiper"; | |||
| import "swiper/dist/css/swiper.css"; | |||
| var timer; | |||
| export default { | |||
| name: "SwiperView", | |||
| components: { | |||
| swiper, | |||
| swiperSlide | |||
| }, | |||
| data() { | |||
| const files = require.context("@/assets/ads", true).keys(); | |||
| let ads = []; | |||
| console.log(ads); | |||
| for (let url in files) { | |||
| let temp = {}; | |||
| console.log("../assets/ads/" + files[url]); | |||
| temp.url = require("../assets/ads/" + files[url].substr(2)); | |||
| temp.type = 0; | |||
| if (files[url].endsWith("mp4")) { | |||
| temp.type = 1; | |||
| } | |||
| ads.push(temp); | |||
| } | |||
| return { | |||
| swiperOption: { | |||
| speed: 1000, | |||
| loop: false, | |||
| observer: true, | |||
| observeParents: true, | |||
| autoplayDisableOnInteraction: false, | |||
| allowTouchMove: false, | |||
| pagination: { | |||
| el: ".swiper-pagination", | |||
| clickable: true | |||
| }, | |||
| on: { | |||
| slideChangeTransitionEnd: () => { | |||
| this.slideChangeTransitionEndHandle(); | |||
| }, | |||
| slideChangeTransitionStart: () => { | |||
| this.slideChangeTransitionStartHandle(); | |||
| }, | |||
| //控制第一个slide切换 | |||
| init: () => { | |||
| this.initHandle(); | |||
| } | |||
| } | |||
| }, | |||
| initOrNot: false, | |||
| mediaLastIndex: 0, | |||
| mediaNews: ads | |||
| }; | |||
| }, | |||
| computed: { | |||
| swiper() { | |||
| return this.$refs.videoSwiper.swiper; | |||
| } | |||
| }, | |||
| watch: { | |||
| mediaNews: { | |||
| handler(newName, oldName) { | |||
| if (newName.length > 0) { | |||
| this.initOrNot = false; | |||
| this.$nextTick(() => { | |||
| this.initOrNot = true; | |||
| }); | |||
| } | |||
| }, | |||
| immediate: true, | |||
| deep: true | |||
| } | |||
| }, | |||
| mounted() {}, | |||
| methods: { | |||
| initHandle() { | |||
| let that = this; | |||
| timer && window.clearTimeout(timer); | |||
| timer = setTimeout(function() { | |||
| let swiper = that.$refs.videoSwiper.swiper; | |||
| that.mediaNewsImgHandle(swiper); | |||
| }, 200); | |||
| }, | |||
| mediaNewsImgHandle(swiper) { | |||
| //刚切换到的activeIndex | |||
| let changePointActiveIndex = swiper.activeIndex; | |||
| if (swiper.activeIndex < this.mediaNews.length - 1) { | |||
| timer && window.clearTimeout(timer); | |||
| timer = setTimeout(function() { | |||
| //要确认changePointActiveIndex是不是还是目前的activeIndex,是的话计时后执行,不是的话不执行 | |||
| if (changePointActiveIndex === swiper.activeIndex) { | |||
| swiper.slideNext(); | |||
| } | |||
| }, 5000); | |||
| } else { | |||
| timer && window.clearTimeout(timer); | |||
| timer = setTimeout(function() { | |||
| if (changePointActiveIndex === swiper.activeIndex) { | |||
| swiper.slideTo(0, 0); | |||
| } | |||
| }, 5000); | |||
| } | |||
| }, | |||
| slideChangeTransitionStartHandle() { | |||
| let swiper = this.$refs.videoSwiper.swiper; | |||
| if (this.mediaNews[this.mediaLastIndex].type === 1) { | |||
| document.getElementsByClassName("multimedia")[this.mediaLastIndex].currentTime = 0; | |||
| } | |||
| }, | |||
| slideChangeTransitionEndHandle() { | |||
| console.log("end.."); | |||
| let that = this; | |||
| let swiper = that.$refs.videoSwiper.swiper; | |||
| if (this.mediaNews[swiper.activeIndex].type === 0) { | |||
| this.mediaNewsImgHandle(swiper); | |||
| } else { | |||
| if (this.mediaLastIndex.type === 1) { | |||
| document.getElementsByClassName("multimedia")[this.mediaLastIndex].pause(); | |||
| } | |||
| document.getElementsByClassName("multimedia")[swiper.activeIndex].removeAttribute("muted"); | |||
| document.getElementsByClassName("multimedia")[swiper.activeIndex].play(); | |||
| // this.playVideo(this.mediaNews[swiper.activeIndex].url); | |||
| } | |||
| this.mediaLastIndex = swiper.activeIndex; | |||
| }, | |||
| endVideo(index) { | |||
| let swiper = this.$refs.videoSwiper.swiper; | |||
| if (index === swiper.activeIndex) { | |||
| if (swiper.activeIndex < this.mediaNews.length - 1) { | |||
| swiper.slideNext(); | |||
| if (this.mediaNews[swiper.activeIndex].type === 1) { | |||
| document.getElementsByClassName("multimedia")[swiper.activeIndex].removeAttribute("muted"); | |||
| document.getElementsByClassName("multimedia")[swiper.activeIndex].play(); | |||
| // this.playVideo(this.mediaNews[swiper.activeIndex].url); | |||
| } | |||
| } else { | |||
| swiper.slideTo(0, 0); | |||
| if (this.mediaNews[swiper.activeIndex].type === 1) { | |||
| document.getElementsByClassName("multimedia")[swiper.activeIndex].removeAttribute("muted"); | |||
| document.getElementsByClassName("multimedia")[swiper.activeIndex].play(); | |||
| // this.playVideo(this.mediaNews[swiper.activeIndex].url); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style> | |||
| .recommendPage .swiper-container { | |||
| position: relative; | |||
| width: 100%; | |||
| height: 1920px; | |||
| background: transparent; | |||
| } | |||
| .recommendPage .swiper-container .swiper-slide { | |||
| width: 100%; | |||
| //line-height: 1920px; | |||
| background: transparent; | |||
| color: #000; | |||
| font-size: 16px; | |||
| text-align: center; | |||
| } | |||
| swiper-slide img { | |||
| object-fit: contain; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,29 @@ | |||
| export default { | |||
| /** | |||
| * @description 是否输出调试信息 | |||
| */ | |||
| isDebuggable: process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test", | |||
| /** | |||
| * @description api请求基础路径 | |||
| */ | |||
| baseUrl: process.env.VUE_APP_SERVER_URL, | |||
| appTitle: "缔智园数字人", | |||
| userType: { | |||
| mgr: 0, | |||
| emp: 1, | |||
| keyVisitor: 2, | |||
| courier: 3, | |||
| blackRole: 4, | |||
| visitor: 5 | |||
| }, | |||
| userTypeCn: { | |||
| 0: "高管", | |||
| 1: "员工", | |||
| 2: "VIP客户", | |||
| 3: "快递员", | |||
| 4: "黑名单", | |||
| 5: "访客" | |||
| } | |||
| }; | |||
| @@ -0,0 +1,21 @@ | |||
| var connection = require("./sql.js"); | |||
| module.exports = { | |||
| query: (callback) => { | |||
| connection.query("select * from cmd order by created_at desc limit 20", function(error, results, fields) { | |||
| console.log(results); | |||
| callback(results); | |||
| }); | |||
| }, | |||
| add: (obj, callback) => { | |||
| connection.query( | |||
| "insert into cmd (question_id, question_text, answer) value (?, ?, ?)", | |||
| [obj.id, obj.text, obj.answer], | |||
| function(error, results) { | |||
| console.log(results); | |||
| callback(results); | |||
| } | |||
| ); | |||
| } | |||
| }; | |||
| @@ -0,0 +1,88 @@ | |||
| const { add, query } = require("../db/dbHelp"); | |||
| const express = require("express"); | |||
| const router = express.Router(); | |||
| // 连接数据库 | |||
| const jsonWrite = function(res, ret) { | |||
| if (typeof ret === "undefined") { | |||
| res.json({ | |||
| code: "1", | |||
| msg: "操作失败" | |||
| }); | |||
| } else { | |||
| res.json(ret); | |||
| } | |||
| }; | |||
| // 接口:增加信息sql,编辑修改信息sql1 | |||
| router.post("/save", (req, res) => { | |||
| const params = req.body; | |||
| add(params, (result) => { | |||
| jsonWrite(res, result); | |||
| }); | |||
| }); | |||
| // 接口:用户管理分页接口查询 | |||
| router.get("/getlist", (req, res) => { | |||
| const params = req.body; | |||
| console.log(params); | |||
| alert("okokokok"); | |||
| query((result) => { | |||
| jsonWrite(res, result); | |||
| }); | |||
| }); | |||
| const db = require("../db/mssql"); //注意改路径 | |||
| router.get("/ms/detailMD", (req, res) => { | |||
| console.log(db.sql); | |||
| //根据时间查询的模板 | |||
| var count = 0; | |||
| var commonResult = []; | |||
| db.sql( | |||
| "SELECT PassType,count(1) as Count FROM HJ_PersonRecognition WHERE DateDiff(dd,DetectTime,getdate())=0 group by PassType order by PassType", | |||
| function(err, result) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| commonResult.push(...result.recordset); | |||
| count++; | |||
| if (count >= 3) { | |||
| res.send(commonResult); | |||
| } | |||
| } | |||
| ); | |||
| db.sql( | |||
| "select '0' as Reserve1, count(1) as cnt from HJ_PersonRecognition as person where person.Reserve1 = '0' and DATEDIFF(DAY, person.DetectTime, GETDATE()) = 0 union select '1' as Reserve1, count(1) as cnt from HJ_PersonRecognition as person where person.Reserve1 = '1' and DATEDIFF(DAY, person.DetectTime, GETDATE()) = 0", | |||
| function(err, result) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| commonResult.push(...result.recordset); | |||
| count++; | |||
| if (count >= 3) { | |||
| res.send(commonResult); | |||
| } | |||
| } | |||
| ); | |||
| db.sql( | |||
| "SELECT '0' as online, count(1) as num FROM HJ_EquipInfo WHERE Status = 1 and EquipTypeID = 3 union SELECT '1' as online, count(1) as num FROM HJ_EquipInfo WHERE (Status = 0 or Status = 2) and EquipTypeID = 3", | |||
| function(err, result) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| commonResult.push(...result.recordset); | |||
| count++; | |||
| if (count >= 3) { | |||
| res.send(commonResult); | |||
| } | |||
| } | |||
| ); | |||
| }); | |||
| module.exports = router; | |||
| @@ -0,0 +1,54 @@ | |||
| /* | |||
| mssql模块简单封装 | |||
| */ | |||
| const mssql = require("mssql"); | |||
| const db = {}; | |||
| const config = { | |||
| user: "sa", | |||
| password: "123456", //改成你自己的 | |||
| server: "192.168.1.254", //改成你自己的 | |||
| database: "Yifangzhongxin", //改成你自己的 | |||
| port: 1433, //改成你自己的 | |||
| options: { | |||
| encrypt: false // Use this if you're on Windows Azure | |||
| }, | |||
| pool: { | |||
| min: 0, | |||
| max: 10, | |||
| idleTimeoutMillis: 3000 | |||
| } | |||
| }; | |||
| //执行sql,返回数据. | |||
| db.sql = function(sql, callBack) { | |||
| const connection = new mssql.ConnectionPool(config, function(err) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| const ps = new mssql.PreparedStatement(connection); | |||
| ps.prepare(sql, function(err) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| ps.execute("", function(err, result) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| ps.unprepare(function(err) { | |||
| if (err) { | |||
| console.log(err); | |||
| callback(err, null); | |||
| return; | |||
| } | |||
| callBack(err, result); | |||
| }); | |||
| }); | |||
| }); | |||
| }); | |||
| }; | |||
| module.exports = db; | |||
| @@ -0,0 +1,10 @@ | |||
| var mysql = require("mysql"); | |||
| // 连接数据库 | |||
| var connection = mysql.createConnection({ | |||
| host: "39.105.85.176", | |||
| user: "root", | |||
| password: "Lecooai@2021", | |||
| database: "vue" | |||
| }); | |||
| module.exports = connection; | |||
| @@ -0,0 +1,70 @@ | |||
| import Vue from "vue"; | |||
| import App from "@/App.vue"; | |||
| import router from "@/router"; | |||
| import _ from "lodash"; | |||
| import "@/plugins/lazyload"; | |||
| import "@/plugins/mintui"; | |||
| import Navigation from "vue-navigation"; | |||
| Vue.use(Navigation, { router }); | |||
| import Axios from "axios"; | |||
| Vue.prototype.$axios = Axios; | |||
| import ElementUI from "element-ui"; //element-ui的全部组件 | |||
| import "element-ui/lib/theme-chalk/index.css"; //element-ui的css | |||
| Vue.use(ElementUI); //使用elementUI | |||
| Vue.config.productionTip = false; | |||
| import jquery from "jquery"; | |||
| Vue.prototype.$ = jquery; | |||
| let app = null; | |||
| let count = 0; | |||
| import "./assets/bootstrap.min.js"; | |||
| /*import socket from "./mixins/socket"; | |||
| Vue.prototype.sock = socket;*/ | |||
| import Alert from "./components/Alert"; | |||
| Vue.use(Alert); | |||
| import store from "./store"; | |||
| function bindData() { | |||
| if (count == 1) { | |||
| app = new Vue({ | |||
| router, | |||
| store, | |||
| render: (h) => h(App) | |||
| }).$mount("#app"); | |||
| global.vm = app; | |||
| } | |||
| } | |||
| Axios.get("./config.json").then((res) => { | |||
| if (res.data) { | |||
| count += 1; | |||
| Vue.prototype.$cmdList = res.data; | |||
| Vue.prototype._ = _; | |||
| bindData(); | |||
| } | |||
| }); | |||
| Date.prototype.Format = function(fmt) { | |||
| var o = { | |||
| "M+": this.getMonth() + 1, // 月份 | |||
| "d+": this.getDate(), // 日 | |||
| "h+": this.getHours(), // 小时 | |||
| "m+": this.getMinutes(), // 分 | |||
| "s+": this.getSeconds(), // 秒 | |||
| "q+": Math.floor((this.getMonth() + 3) / 3), // 季度 | |||
| S: this.getMilliseconds() // 毫秒 | |||
| }; | |||
| if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); | |||
| for (var k in o) | |||
| if (new RegExp("(" + k + ")").test(fmt)) | |||
| fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); | |||
| return fmt; | |||
| }; | |||
| export default Vue; | |||
| @@ -0,0 +1,274 @@ | |||
| import { handleNlp } from "../utils/handleNlp"; | |||
| import { handleQEvent } from "../utils/handleQ"; | |||
| import { containObjByCode } from "../utils/common"; | |||
| import { mapMutations, mapState } from "vuex"; | |||
| import Cloudia from "../api/cloudia-sdk-v1.4.1"; | |||
| const FormData = require("form-data"); | |||
| /** | |||
| * 通用的方法和一些与业务无关的工具方法 | |||
| * | |||
| */ | |||
| const mixin = { | |||
| data() { | |||
| return { | |||
| playerOptions: {}, | |||
| devCode: "", | |||
| picUrl: "", | |||
| picVisible: false, | |||
| videoVisible: false, | |||
| jsPlayTtsEnabled: true, | |||
| jsHandleStateEnabled: false | |||
| }; | |||
| }, | |||
| computed: { | |||
| ...mapState({ | |||
| debugFlag: "debug" | |||
| }) | |||
| }, | |||
| methods: { | |||
| ...mapMutations(["setLastPerson", "setProgressPerson", "setLocalDevInfo", "setPad", "setDebug"]), | |||
| jsonpRequest(url) { | |||
| return new Promise((resolve, reject) => { | |||
| const callbackName = "jsonpCallback"; // 回调函数名 | |||
| // 创建一个 script 元素 | |||
| const script = document.createElement("script"); | |||
| script.src = url + (url.indexOf("?") === -1 ? "?" : "&") + "callback=" + callbackName; | |||
| document.body.appendChild(script); | |||
| // 设置回调函数 | |||
| window[callbackName] = (data) => { | |||
| delete window[callbackName]; | |||
| document.body.removeChild(script); | |||
| resolve(data); | |||
| }; | |||
| // 请求失败处理 | |||
| script.onerror = (error) => { | |||
| delete window[callbackName]; | |||
| document.body.removeChild(script); | |||
| reject(error); | |||
| }; | |||
| }); | |||
| }, | |||
| cloudiaInit() { | |||
| let that = this; | |||
| try { | |||
| if (Cloudia && Cloudia.init) { | |||
| Cloudia.init( | |||
| function(cmd, params) { | |||
| switch (cmd) { | |||
| case "onBind": | |||
| that.character = params.currentCharacter; | |||
| that.allClothes = window.cloudiaConfig.allClothes; | |||
| break; | |||
| case "setState": | |||
| if ("GREET" == params.state) { | |||
| that.tabIndex = 0; | |||
| that.$parent.$data.sleep = false; | |||
| // that.isSpeaking = false; | |||
| } | |||
| that.$parent.$data.stateVal = params.state; | |||
| break; | |||
| case "event": | |||
| that.cloudiaEvents[params["name"]] = params["info"]["status"]; | |||
| break; | |||
| case "nlp": | |||
| handleNlp(that, params); | |||
| break; | |||
| case "event": | |||
| //人脸处理不通过达闼 | |||
| if ("faceDetected" == params.name) { | |||
| } | |||
| break; | |||
| case "q": | |||
| handleQEvent(that, params); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| }, | |||
| { | |||
| jsHandleTts: that.jsPlayTtsEnabled, | |||
| jsHandleState: that.jsHandleStateEnabled, | |||
| cameraMode: that.cameraMode | |||
| } | |||
| ); | |||
| Cloudia.setWalkGreetingEnable(false); | |||
| Cloudia.setAsrWakeUpEnable(true); | |||
| } | |||
| } catch (e) { | |||
| that.$parent.consoleLog(e.message); | |||
| } | |||
| }, | |||
| mixinMethod() { | |||
| let that = this; | |||
| if (!(that.localDev && that.$pad)) { | |||
| that.cloudiaInit(); | |||
| that.$nextTick(function() { | |||
| Cloudia.getRobotInfoConfig("RobotId").then(function(ReturnValue) { | |||
| let RobotId = ReturnValue.ReturnValue; | |||
| // let RobotId = "864972045000846"; | |||
| that.devCode = RobotId; | |||
| //绑定本设备相关信息 | |||
| that.localDevInfo = containObjByCode(that.$cmdList["devMappings"], that.devCode); | |||
| that.setLocalDevInfo(that.localDevInfo); | |||
| that.$axios.get("/srv/api/device/getPadExt?deviceId=" + RobotId).then((res) => { | |||
| if (res.data) { | |||
| that.$pad = res.data.data; | |||
| console.log(JSON.stringify(that.$pad)); | |||
| that.setPad(that.$pad); | |||
| that.otherLog2 = "padInfo:" + JSON.stringify(that.$pad); | |||
| } | |||
| }); | |||
| }); | |||
| that.updLocation(); | |||
| }); | |||
| } | |||
| }, | |||
| setVideoVisible(visible) { | |||
| this.videoVisible = visible; | |||
| }, | |||
| setTabindex(tabIndex) { | |||
| this.tabIndex = tabIndex; | |||
| }, | |||
| playVideo(src) { | |||
| if (src) { | |||
| this.picVisible = false; | |||
| this.tabIndex = 5; | |||
| this.playerOptions = { | |||
| muted: false, | |||
| language: "zh-CN", | |||
| playbackRates: [0.5, 1.0, 1.5, 2.0], | |||
| autoplay: true, | |||
| controls: false, | |||
| sources: [ | |||
| { | |||
| type: "video/mp4", | |||
| src: src | |||
| } | |||
| ] | |||
| }; | |||
| this.videoVisible = true; | |||
| this.enterImmerseMode(true); | |||
| } | |||
| }, | |||
| playPic(src) { | |||
| if (src) { | |||
| this.videoVisible = false; | |||
| this.tabIndex = 5; | |||
| this.picUrl = src; | |||
| this.picVisible = true; | |||
| var that = this; | |||
| setTimeout(function() { | |||
| that.picVisible = false; | |||
| }, 5000); | |||
| } | |||
| }, | |||
| enterImmerseMode(mode) { | |||
| Cloudia.enterImmerseMode(mode); | |||
| }, | |||
| queryNlp() { | |||
| let that = this; | |||
| this.$axios.get(this.$cmdList.serverDb + "/api/db/getlist", {}).then((res) => { | |||
| that.nplList = res.data; | |||
| }); | |||
| }, | |||
| changeCharacter() { | |||
| var characters = this.$cmdList["characters"]; | |||
| if (this.characterInd >= characters.length - 1) { | |||
| this.characterInd = 0; | |||
| } else { | |||
| this.characterInd++; | |||
| } | |||
| Cloudia.setCharacter(characters[this.characterInd]); | |||
| this.character = characters[this.characterInd]; | |||
| this.clothesInd = 0; | |||
| var clothes = this.$cmdList["clothes"][this.character]; | |||
| Cloudia.setClothes(clothes[this.clothesInd]); | |||
| }, | |||
| changeCloth() { | |||
| var clothes = this.$cmdList["clothes"][this.character]; | |||
| if (this.clothesInd >= clothes.length - 1) { | |||
| this.clothesInd = 0; | |||
| } else { | |||
| this.clothesInd++; | |||
| } | |||
| Cloudia.setClothes(clothes[this.clothesInd]); | |||
| }, | |||
| scrollMsgToBottom() { | |||
| this.tabIndex = 0; | |||
| let that = this; | |||
| this.$nextTick(() => { | |||
| let middle = that.$refs["middle"]; | |||
| if (middle) middle.scrollTop = middle.scrollHeight; | |||
| }); | |||
| }, | |||
| clickEvent() { | |||
| this.clickNum++; | |||
| if (this.clickNum >= 20) { | |||
| this.close(); | |||
| this.clickNum = 0; | |||
| // alert(this.clickNum); | |||
| } | |||
| if (this.clickNum >= 15) { | |||
| // this.setDebug(!this.debugFlag); | |||
| this.showMsg = !this.showMsg; | |||
| this.$parent.$data.debug = !this.$parent.$data.debug; | |||
| } | |||
| }, | |||
| homeEvent() { | |||
| // this.$parent.$data.ws.send("pic1"); | |||
| this.jsonpRequest(this.$cmdList.handleUrl + "pic1") | |||
| .then((data) => {}) | |||
| .catch((error) => {}); | |||
| }, | |||
| companyEvent() { | |||
| let that = this; | |||
| this.jsonpRequest(this.$cmdList.handleUrl + "pic2") | |||
| .then((data) => {}) | |||
| .catch((error) => {}); | |||
| this.jsonpRequest(this.$cmdList.handleUrl + "name=play") | |||
| .then((data) => {}) | |||
| .catch((error) => {}); | |||
| this.jsonpRequest(this.$cmdList.handleUrl + "speed=0.13") | |||
| .then((data) => {}) | |||
| .catch((error) => {}); | |||
| if (that.$parent.$data.isSpeaking) { | |||
| that.$parent.setIsSpeaking(false); | |||
| window.Cloudia.stopPlayTts(); | |||
| that.jsonpRequest(this.$cmdList.handleUrl + "name=stop") | |||
| .then((data) => {}) | |||
| .catch((error) => {}); | |||
| } else { | |||
| setTimeout(function() { | |||
| that.addMsgList( | |||
| 2, | |||
| "协合新能源集团创立于二零零六年,自创立之初便致力于风电项目的建设,并在二零零六年成功投产了首个风电项目、、位于辽宁昌图的五十兆瓦风电场。随后的二零零七年,集团于香港联交所上市,成为中国内地和香港市场上首家风电上市企业。二零零八年,集团初步形成了纵向一体化风电产业链,包括制造、开发、建设和营运等环节、在二零零九年集团进入发展的快车道,并与多家国内外大型发电企业建立战略合作关系,权益装机容量首次超过五百兆瓦。在二零一零年,集团发展达到了阶段高峰,年度净利润达到四点二七亿港币,累计投资的风电项目数量达到二十七个,共计装机容量达到一千七百一十兆瓦,并与国际金融公司IFC实现合作。随后,在二零一一年,集团积极实施向南发展和向太阳能发展的战略,成功并网了首个光伏电站。二零一二年,首个海外光伏项目、美国纽约分布式光伏项目也投产、、经过多年实践,集团资产结构和资产质量持续优化,并于二零一六年成功发行了国内首单非金融企业绿色债券。在二零一七年,集团获得了国际信用评级机构的BB评级,并发布了智慧能源解决方案POWER加。二零一八年,权益装机容量突破了二百万千瓦。二零一九年,集团首个平价项目当年开工、当年投产,这展示了我们已迈入平价时代。二零二零年,尽管受到疫情影响,集团仍始终坚持项目的建设,并取得了资产结构和质量的极大提升,实现持续滚动发展。二零二一年,集团实现了年投产容量百万千瓦,并创造了二百兆瓦电站工程最短工期记录。在二零二二年,开发指标创下了历史新高,基地项目申报实现零的突破,连续实现年投产容量超过百万千瓦,并在MSCI的ESG评级中获评为A级、、协合新能源集团从成立伊始就以发展绿色能源为己任,追求可持续发展,而历经多年的努力和拼搏,已初步建立了一条完整的绿色发展产业链,未来也将围绕用清洁能源创造更好未来的核心价值观,促进企业与社会的可持续发展。" | |||
| ); | |||
| }, 800); | |||
| } | |||
| }, | |||
| updLocation() { | |||
| window.Cloudia.setBgWallResource("Landscape"); | |||
| // window.Cloudia.updateCharacterLocation(-500, 0, 450); | |||
| }, | |||
| _clearCache() { | |||
| localStorage.clear(); | |||
| }, | |||
| close() { | |||
| Cloudia.restartH5(); | |||
| } | |||
| } | |||
| }; | |||
| export default mixin; | |||
| @@ -0,0 +1,95 @@ | |||
| let isConnect = false; | |||
| const socket = { | |||
| components: {}, | |||
| websock: null, | |||
| times: 0, | |||
| timeout: 10000, | |||
| timeoutObj: null, | |||
| serverTimeoutObj: null, | |||
| heartCheck: { | |||
| reset: function() { | |||
| console.log(this); | |||
| clearTimeout(this.timeoutObj); | |||
| clearTimeout(this.serverTimeoutObj); | |||
| return this; | |||
| }, | |||
| start: function() { | |||
| var self = this; | |||
| this.timeoutObj = setTimeout(function() { | |||
| //这里发送一个心跳,后端收到后,返回一个心跳消息, | |||
| //onmessage拿到返回的心跳就说明连接正常 | |||
| self.websock.send("ping"); | |||
| self.serverTimeoutObj = setTimeout(function() { | |||
| //如果超过一定时间还没重置,说明后端主动断开了 | |||
| self.websock.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次 | |||
| }, self.timeout); | |||
| }, this.timeout); | |||
| } | |||
| }, | |||
| initWebSocket() { | |||
| try { | |||
| // const path = Config.padSocketUrl; // 后台给的websocket的ip地址 | |||
| console.log("---==-=-=-=-="); | |||
| // if (this.$pad && this.$pad.ipAddr) { | |||
| const path = "ws://192.168.33.251:9090"; // 后台给的websocket的ip地址 | |||
| this.websock = new WebSocket(path); | |||
| /*this.websock.onmessage = this.websocketOnMessage;*/ | |||
| this.websock.onopen = this.websocketOnOpen; | |||
| this.websock.onerror = this.websocketOnError; | |||
| this.websock.onclose = this.websocketClose; | |||
| /*} else { | |||
| this.reConnect(); | |||
| }*/ | |||
| } catch (e) { | |||
| this.reConnect(); | |||
| console.log(e); | |||
| } | |||
| }, | |||
| reConnect() { | |||
| if (isConnect) { | |||
| this.consoleLog("重连成功!"); | |||
| return; | |||
| } //如果已经连上就不在重连了 | |||
| window.socketTimer && window.clearTimeout(window.socketTimer); | |||
| let that = this; | |||
| window.socketTimer = setTimeout(function() { | |||
| // 延迟5秒重连 避免过多次过频繁请求重连 | |||
| that.initWebSocket(); | |||
| that.times++; | |||
| }, 5000); | |||
| }, | |||
| // 连接建立成功的信号 | |||
| websocketOnOpen() { | |||
| console.log(22); | |||
| isConnect = true; | |||
| this.times = 0; | |||
| this.heartCheck.reset().start(); | |||
| }, | |||
| // 连接建立失败重连 | |||
| websocketOnError() { | |||
| // 如果报错的话,在这里就可以重新初始化websocket,这就是断线重连 | |||
| this.websock = null; | |||
| isConnect = false; //连接断开修改标识 | |||
| this.reConnect(); //连接错误 需要重连 | |||
| }, | |||
| // 数据发送 | |||
| websocketSend(Data) { | |||
| if (typeof Data == "object") { | |||
| Data = JSON.stringify(Data); | |||
| } | |||
| this.otherLog = "send to serv:" + Data; | |||
| if (this.socket && this.socket.readyState === 1) { | |||
| this.websock.send(Data); // Data变量就是你想对后台说些啥,根据后端给你的接口文档传值进行交互 | |||
| } | |||
| }, | |||
| // 关闭的信号 | |||
| websocketClose() { | |||
| this.websock = null; | |||
| isConnect = false; //连接断开修改标识 | |||
| this.reConnect(); //服务器主动断开的情况下,需要重连 | |||
| } | |||
| }; | |||
| export default socket; | |||
| @@ -0,0 +1,5 @@ | |||
| // 引入各种函数,便于做成npm包 | |||
| // indexedDB 部分 | |||
| import MetaHelp from "./nf-meta/help.js"; | |||
| export { MetaHelp }; | |||
| @@ -0,0 +1,87 @@ | |||
| import { reactive } from "vue"; | |||
| // 从json文件加载meta | |||
| import loadMetaFormJson from "./loadmeta-json.js"; | |||
| // 从 webSQL加载meta | |||
| import loadMetaFormSQL from "./loadmeta-sql.js"; | |||
| // 把meta存入 indexedDB | |||
| import { saveMetaAll, loadMeta } from "./savemeta-db.js"; | |||
| /** | |||
| * meta的help | |||
| * * 从 json 加载 meta,存入 indexedDB | |||
| * * 从 webSQL 加载 meta,存入 indexedDB | |||
| * * 从 indexedDB 加载 meta, | |||
| * * 把 meta 存入状态 | |||
| */ | |||
| export default class MetaHelp { | |||
| constructor(url) { | |||
| (this.jsonUrl = url), // json 的路径,axios 加载用 | |||
| (this.sqlHelp = null), // 访问 webSQL | |||
| (this.dbHelp = null), // 访问 indexedDB | |||
| (this.meta = reactive({ | |||
| menu: {}, | |||
| module: {}, | |||
| service: {} | |||
| })); | |||
| } | |||
| /** | |||
| * 从 indexedDB 里面加载 meta | |||
| * @returns | |||
| */ | |||
| async loadMetaFromDB() { | |||
| return await loadMeta(this.dbHelp); | |||
| } | |||
| /** | |||
| * 把 meta 存入 state | |||
| * @param {*} state 状态 | |||
| * @param {*} meta 要存入的 meta。menu:数组;module:对象;service:对象 | |||
| */ | |||
| toState(state, meta) { | |||
| // 存入菜单,需要去重 | |||
| meta.menu.forEach((_menu) => { | |||
| if (state.menu.findIndex((m) => m.id === _menu.id) < 0) { | |||
| state.menu.push(_menu); | |||
| } | |||
| }); | |||
| // 存入模块 | |||
| Object.assign(state.module, meta.module); | |||
| // 存入服务 | |||
| Object.assign(state.service, meta.service); | |||
| } | |||
| /** | |||
| * 从 webSQL 加载 meta,然后存入 indexedDB,并且返回 meta | |||
| * @param {*} moduleId | |||
| */ | |||
| async sqlToDB(moduleId = null) { | |||
| console.log("MetaHelp 的 sqlToDB", this); | |||
| const meta = await loadMetaFormSQL(this.sqlHelp); | |||
| console.log("MetaHelp 的 SQL 读取出来的 meta ", meta); | |||
| // 遍历,添加到 indexedDB | |||
| saveMetaAll(this.dbHelp, meta); | |||
| return meta; | |||
| } | |||
| /** | |||
| * 从 json 加载 meta,然后存入 indexedDB,并且返回 meta | |||
| * @param {*} moduleId 模块ID | |||
| * @returns | |||
| */ | |||
| async loagMetaFromJson(moduleId = null) { | |||
| console.log("MetaHelp 的 sqlToDB", this); | |||
| const meta = await loadMetaFormJson(this.jsonUrl); | |||
| console.log("MetaHelp 的 从json加载的meta:", meta); | |||
| // 遍历,添加到 indexedDB | |||
| saveMetaAll(this.dbHelp, meta); | |||
| return meta; | |||
| } | |||
| } | |||
| @@ -0,0 +1,138 @@ | |||
| // axios | |||
| import axios from "axios"; | |||
| /** | |||
| * 加载 json 文件 整理成 meta 的格式,返回 | |||
| */ | |||
| /** | |||
| * 01 获取 json文件 的目录,因为json文件比较多,还希望实现热更新,所以做了个加载目录。 | |||
| * * 应该加个随机数,便于即使更新。 | |||
| * * 因为有缓存,所以不用怕过多访问的问题。 | |||
| */ | |||
| const _loadFileDirectory = (jsonURL) => { | |||
| return new Promise((resolve, reject) => { | |||
| const _url = `${jsonURL}/dir.json?v=1`; | |||
| axios | |||
| .get(_url) | |||
| .then((res) => { | |||
| // console.log('dir -- 目录:', res) | |||
| resolve(res.data); | |||
| }) | |||
| .catch((res) => { | |||
| reject(res); | |||
| }); | |||
| }); | |||
| }; | |||
| /** | |||
| * 整理加载的json,转成meta格式 | |||
| */ | |||
| const _format = (res) => { | |||
| const meta = { | |||
| service: {}, | |||
| module: {}, | |||
| menu: res[0].data.menu | |||
| }; | |||
| // 遍历meta,进行分类 | |||
| res.forEach((re, index) => { | |||
| if (index === 0) return; // 第一个是菜单, | |||
| const model = re.data; | |||
| if (typeof model.actions !== "undefined") { | |||
| // 后端API | |||
| meta.service[model.moduleId] = model; | |||
| } else { | |||
| // 模块的 meta | |||
| if (typeof meta.module[model.moduleId] === "undefined") { | |||
| meta.module[model.moduleId] = { | |||
| moduleId: model.moduleId | |||
| }; | |||
| } | |||
| // 开始判断 | |||
| if (typeof model.btnOrder === "object") { | |||
| // 按钮 | |||
| meta.module[model.moduleId].button = model; | |||
| } else if (typeof model.quickFind === "object") { | |||
| // 查询 | |||
| meta.module[model.moduleId].find = model; | |||
| } else if (typeof model.idName === "string") { | |||
| // 列表 | |||
| meta.module[model.moduleId].grid = model; | |||
| } else if (typeof model.formId !== "undefined") { | |||
| // 表单 | |||
| if (typeof meta.module[model.moduleId].forms === "undefined") { | |||
| meta.module[model.moduleId].forms = {}; | |||
| } | |||
| meta.module[model.moduleId].forms[model.formId] = model; | |||
| } | |||
| } | |||
| }); | |||
| return meta; | |||
| }; | |||
| /** | |||
| * axios 读取json文件,然后返回 | |||
| * @param {*} josnDir 前端模块meta 的文件夹 | |||
| * @param {*} serviveDir 后端服务meta 的文件夹 | |||
| * @param {*} jsonURL 项目meta的url | |||
| * @returns | |||
| */ | |||
| const _loadMeta = (josnDir, serviveDir, jsonURL) => { | |||
| return new Promise((resolve, reject) => { | |||
| const actionName = ["button", "find", "grid"]; | |||
| // 加载json的 请求的 数组,交给 Promise.all 使用 | |||
| const getMetaAxios = []; | |||
| // 加入导航菜单 | |||
| const _url = `${jsonURL}/menu.json`; | |||
| getMetaAxios.push(axios.get(_url)); | |||
| // 前端meta 请求 加入数组 | |||
| for (const key in josnDir) { | |||
| const meta = josnDir[key]; // 模块需要的表单 | |||
| // 添加按钮、列表、查询 | |||
| actionName.forEach((action) => { | |||
| const _url = `${jsonURL}module/${key}/${action}.json`; | |||
| getMetaAxios.push(axios.get(_url)); | |||
| }); | |||
| // 添加表单 | |||
| meta.forEach((form) => { | |||
| const _url = `${jsonURL}module/${key}/${form}.json`; | |||
| getMetaAxios.push(axios.get(_url)); | |||
| }); | |||
| } | |||
| // 后端API的meta 请求,也加入数组 | |||
| serviveDir.forEach((api) => { | |||
| const _url = `${jsonURL}service/${api}.json`; | |||
| getMetaAxios.push(axios.get(_url)); | |||
| }); | |||
| // 一起发起所有json文件的请求 | |||
| Promise.all(getMetaAxios).then((res) => { | |||
| // console.log('data:', res) | |||
| if (res[0].status === 200) { | |||
| // statusText | |||
| // json 数据 转换成 meta 格式 | |||
| const meta = _format(res); | |||
| console.log("----json--处理好后的--meta:--", meta); | |||
| // 返回 meta | |||
| resolve(meta); | |||
| } | |||
| }); | |||
| }); | |||
| }; | |||
| /** | |||
| * 加载josn文件,整理后变成meta格式 | |||
| * @param {string} jsonURL 项目meta的url | |||
| * @returns 整理好的meta | |||
| */ | |||
| const loadMetaFormJson = async (jsonURL) => { | |||
| // 获取json的文件目录,便于 axios 加载 | |||
| const dir = await _loadFileDirectory(jsonURL); | |||
| return await _loadMeta(dir.jsonDir, dir.serviceDir, jsonURL); | |||
| }; | |||
| export default loadMetaFormJson; | |||
| @@ -0,0 +1,209 @@ | |||
| /** | |||
| * 从 SQL 里面加载数据,整理成 meta 的格式 | |||
| */ | |||
| /** | |||
| * 把 webSQL 里的【菜单】数据变成 meta 的格式 | |||
| */ | |||
| const _getMenuMetaBySQL = (data) => { | |||
| const menu = []; | |||
| data.forEach((m) => { | |||
| menu.push({ | |||
| id: m.moduleId, | |||
| componentKind: m.componentKind, | |||
| icon: m.icon, | |||
| moduleLevel: m.moduleLevel, | |||
| parentId: m.parentId, | |||
| title: m.title | |||
| }); | |||
| }); | |||
| return menu; | |||
| }; | |||
| /** | |||
| * 把 webSQL 里的【按钮】数据变成 meta 的格式 | |||
| */ | |||
| const _getButtonMetaBySQL = (data) => { | |||
| const meta = { | |||
| moduleId: data.moduleId, | |||
| btnOrder: [], | |||
| itemMeta: {} | |||
| }; | |||
| data.forEach((btn) => { | |||
| meta.btnOrder.push(btn.buttonId); | |||
| meta.itemMeta[btn.buttonId] = btn; | |||
| }); | |||
| return meta; | |||
| }; | |||
| /** | |||
| * 把 webSQL 里的【列表】数据变成 meta 的格式 | |||
| */ | |||
| const _getGridMetaBySQL = (grid, item) => { | |||
| const meta = grid[0]; | |||
| meta.colOrder = meta.colOrder.split(","); | |||
| meta.itemMeta = {}; | |||
| // meta.quickFind = meta.quickFind | |||
| item.forEach((ctl) => { | |||
| // meta.colOrder.push(ctl.columnId) | |||
| meta.itemMeta[ctl.columnId] = ctl; | |||
| }); | |||
| return meta; | |||
| }; | |||
| /** | |||
| * 把 webSQL 里的【查询】数据变成 meta 的格式 | |||
| */ | |||
| const _getFindMetaBySQL = (find, item) => { | |||
| const meta = find[0]; | |||
| meta.allFind = meta.allFind.split(","); | |||
| meta.quickFind = meta.quickFind.split(","); | |||
| meta.itemMeta = {}; | |||
| item.forEach((ctl) => { | |||
| // meta.quickFind.push(ctl.columnId) | |||
| // meta.allFind.push(ctl.columnId) | |||
| meta.itemMeta[ctl.columnId] = ctl; | |||
| }); | |||
| return meta; | |||
| }; | |||
| /** | |||
| * 把 webSQL 里的【表单】数据变成 meta 的格式 | |||
| * * form 数组,包含多个 formId | |||
| */ | |||
| const _getFormMetaBySQL = (forms, items) => { | |||
| const meta = {}; | |||
| forms.forEach((fm) => { | |||
| meta[fm.formId] = fm; | |||
| meta[fm.formId].colOrder = meta[fm.formId].colOrder.split(","); | |||
| meta[fm.formId].itemMeta = {}; | |||
| items | |||
| .filter((a) => a.formId === fm.formId) | |||
| .forEach((ctl) => { | |||
| meta[fm.formId].itemMeta[ctl.columnId] = ctl; | |||
| }); | |||
| }); | |||
| return meta; | |||
| }; | |||
| /** | |||
| * 把 webSQL 里的【actions】数据变成 meta 的格式 | |||
| */ | |||
| const _getActionMetaBySQL = (data) => { | |||
| const actions = {}; | |||
| data.forEach((action) => { | |||
| actions[action.actionId] = { | |||
| actionName: action.actionName, | |||
| kind: action.kind, | |||
| model: action.modelId | |||
| }; | |||
| }); | |||
| return actions; | |||
| }; | |||
| /** | |||
| * 把 webSQL 里的【models】数据变成 meta 的格式 | |||
| */ | |||
| const _getModelMetaBySQL = (data) => { | |||
| const models = {}; | |||
| data.forEach((model) => { | |||
| models[model.actionId] = { | |||
| tableName: model.tableName, | |||
| idKey: model.idKey, | |||
| cols: model.cols, | |||
| pager: { | |||
| orderBy: { roleId: false }, | |||
| pagerIndex: 1, | |||
| pagerSize: model.pagerSize, | |||
| pagerTotal: 100 | |||
| }, | |||
| query: {} | |||
| }; | |||
| }); | |||
| return models; | |||
| }; | |||
| /** | |||
| * 开发模式:从 webSQL 读取数据,转换成meta 的格式 | |||
| * * 菜单meta、模块meta、service的meta | |||
| * * 从 SQL 加载数据,转换格式,返回。 | |||
| */ | |||
| const loadMetaFormSQL = async (sqlHelp) => { | |||
| console.log("------webSQL 的 sqlHelp", sqlHelp); | |||
| const tables = sqlHelp._tables; | |||
| // 存放 SQL 里面的 meta | |||
| const meta = {}; | |||
| // 获取所有数据库里的 meta 数据 | |||
| for (const key in tables) { | |||
| const tt = tables[key]; | |||
| const tmp = await tt.list(); | |||
| meta[key] = Array.from(tmp); | |||
| } | |||
| console.log("------controller 的 meta", meta); | |||
| // 返回的 meta | |||
| const reMeta = { | |||
| menu: _getMenuMetaBySQL(meta.nf_module), // 记录菜单 | |||
| module: {}, // 模块 | |||
| service: {} // service | |||
| }; | |||
| const _tmp = (name, modId) => { | |||
| return meta[name].filter((a) => a.moduleId === modId); | |||
| }; | |||
| // 遍历菜单,变成 meta 的格式 | |||
| for (let i = 0; i < meta.nf_module.length; i++) { | |||
| const modId = meta.nf_module[i].moduleId; | |||
| const __moduleMeta = { | |||
| // 记录模块的meta | |||
| moduleId: modId, | |||
| pager: {}, | |||
| button: {}, | |||
| grid: {}, | |||
| find: {}, | |||
| forms: {} | |||
| }; | |||
| // 分页 | |||
| // __moduleMeta.pager = this._getButtonMetaBySQL(meta[151].filter((a) => a.moduleId === modId)) | |||
| // 列表 | |||
| __moduleMeta.grid = _getGridMetaBySQL(_tmp("v_module_grid", modId), _tmp("v_module_grid_item", modId)); | |||
| // 按钮 | |||
| __moduleMeta.button = _getButtonMetaBySQL(_tmp("v_module_button", modId)); | |||
| // 查询 | |||
| __moduleMeta.find = _getFindMetaBySQL(_tmp("v_module_find", modId), _tmp("v_module_find_item", modId)); | |||
| // 表单 | |||
| __moduleMeta.forms = _getFormMetaBySQL(_tmp("v_module_form", modId), _tmp("v_module_form_item", modId)); | |||
| // 记录 | |||
| reMeta.module[modId] = __moduleMeta; | |||
| } | |||
| // 遍历service,整理后端api | |||
| for (let i = 0; i < meta.v_service.length; i++) { | |||
| const service = meta.v_service[i]; | |||
| const serviceId = service.serviceId; | |||
| // 记录service | |||
| const serviceMeta = { | |||
| moduleId: serviceId, | |||
| moduleName: service.serviceName, | |||
| actions: {}, | |||
| models: {} | |||
| }; | |||
| const _tmp = (id) => { | |||
| return meta[id].filter((a) => a.serviceId === serviceId); | |||
| }; | |||
| serviceMeta.actions = _getActionMetaBySQL(_tmp("v_service_action", serviceId)); | |||
| serviceMeta.models = _getModelMetaBySQL(_tmp("v_service_model", serviceId)); | |||
| reMeta.service[serviceId] = serviceMeta; | |||
| } | |||
| // 返回整理好的 meta | |||
| return reMeta; | |||
| }; | |||
| export default loadMetaFormSQL; | |||
| @@ -0,0 +1,100 @@ | |||
| /** | |||
| * saveMeta:把 meta 的一个属性 存入 indexedDB 的一个对象仓库 | |||
| * saveMetaAll:把 meta 存入 indexedDB | |||
| * loadMeta:从 indexedDB 加载meta | |||
| */ | |||
| /** | |||
| * json、webSQL 的 meta,存入 indexedDB | |||
| * @param {help} help 访问 indexedDB 的 help | |||
| * @param {string} storeName 仓库名称 | |||
| * @param {object} meta 要存入的对象,对象集合 | |||
| * @returns 仅通知 | |||
| */ | |||
| const saveMeta = (help, storeName, meta) => { | |||
| const idName = { | |||
| // 主键名称的字典 | |||
| menuMeta: "id", | |||
| moduleMeta: "moduleId", | |||
| serviceMeta: "moduleId" | |||
| }; | |||
| return new Promise((resolve, reject) => { | |||
| let count = 0; | |||
| help.beginInit(storeName).then((store) => { | |||
| count += Object.keys(meta).length; | |||
| if (count === 0) { | |||
| resolve(null); | |||
| } | |||
| for (const key in meta) { | |||
| // 先判断有没有,没有add;有了put | |||
| store.get(meta[key][idName[storeName]]).onsuccess = (event) => { | |||
| // console.log('====== 要添加的key:', storeName + '_' + meta[key][idName[storeName]]) | |||
| // console.log('====== 获取的 结果:', event.target.result) | |||
| if (typeof event.target.result === "undefined") { | |||
| // 添加 | |||
| store.add(meta[key]).onsuccess = (event) => { | |||
| // 添加一条meta | |||
| count -= 1; | |||
| if (count === 0) { | |||
| resolve(event.target.result); | |||
| } | |||
| }; | |||
| } else { | |||
| // 修改 | |||
| store.put(meta[key]).onsuccess = (event) => { | |||
| // 修改一条meta | |||
| count -= 1; | |||
| if (count === 0) { | |||
| resolve(event.target.result); | |||
| } | |||
| }; | |||
| } | |||
| }; | |||
| } | |||
| }); | |||
| }); | |||
| }; | |||
| /** | |||
| * 把 meta 存入 indexedDB | |||
| * @param {*} help indexedDB 的 help | |||
| * @param {*} meta 要存入的 meta | |||
| */ | |||
| const saveMetaAll = async (help, meta) => { | |||
| await saveMeta(help, "serviceMeta", meta.service); | |||
| await saveMeta(help, "moduleMeta", meta.module); | |||
| await saveMeta(help, "menuMeta", meta.menu); | |||
| }; | |||
| /** | |||
| * 从 indexedDB 里面加载 meta,并且返回 | |||
| * @param {*} help indexedDB 的 help | |||
| * @returns | |||
| */ | |||
| const loadMeta = async (help) => { | |||
| const state = { | |||
| menu: [], | |||
| module: {}, | |||
| service: {} | |||
| }; | |||
| state.menu = await help.getModel("menuMeta"); | |||
| const _module = await help.getModel("moduleMeta"); | |||
| const _service = await help.getModel("serviceMeta"); | |||
| for (const key in _module) { | |||
| const m = _module[key]; | |||
| state.module[m.moduleId] = m; | |||
| } | |||
| for (const key in _service) { | |||
| const s = _service[key]; | |||
| state.service[s.moduleId] = s; | |||
| } | |||
| return state; | |||
| }; | |||
| export { | |||
| saveMetaAll, // 存入 meta | |||
| saveMeta, // 存入一个属性 | |||
| loadMeta // 加载 meta | |||
| }; | |||
| @@ -0,0 +1,114 @@ | |||
| /** | |||
| * 一般查询方式的字典 | |||
| */ | |||
| const _find = { | |||
| 401: (colValue, key) => colValue === key, // = | |||
| 403: (colValue, key) => colValue.includes(key), // 包含 | |||
| 405: (colValue, key) => colValue.indexOf(key) === 0, // 起始于 | |||
| 406: (colValue, key) => colValue.indexOf(key) + key.length === colValue.length, // 结束于 | |||
| 413: (colValue, key) => colValue > key, // > | |||
| 414: (colValue, key) => colValue >= key, // >= | |||
| 415: (colValue, key) => colValue < key, // < | |||
| 416: (colValue, key) => colValue <= key, // <= | |||
| 417: (colValue, key) => key[0] <= colValue && colValue <= key[1], // between | |||
| 418: (colValue, key) => key[0] < colValue && colValue <= key[1], // a < x <= b | |||
| 419: (colValue, key) => key[0] <= colValue && colValue < key[1], // a <= x < b | |||
| 420: (colValue, key) => key[0] < colValue && colValue < key[1] // a < x < b | |||
| }; | |||
| /** | |||
| * IDBKeyRange 的字典 | |||
| */ | |||
| const _dicRange = { | |||
| 401: (_value) => IDBKeyRange.only(_value), // = | |||
| // 403: (_value) => IDBKeyRange.only(_value), | |||
| // 405: (_value) => IDBKeyRange.only(_value), | |||
| // 406: (_value) => IDBKeyRange.only(_value), | |||
| 413: (_value) => IDBKeyRange.lowerBound(_value, true), // > | |||
| 414: (_value) => IDBKeyRange.lowerBound(_value), // >= | |||
| 415: (_value) => IDBKeyRange.upperBound(_value, true), // < | |||
| 416: (_value) => IDBKeyRange.upperBound(_value), // <= | |||
| 417: (_value) => IDBKeyRange.bound(_value[0], _value[1]), // between | |||
| 418: (_value) => IDBKeyRange.bound(_value[0], _value[1], true, false), // a < x <= b | |||
| 419: (_value) => IDBKeyRange.bound(_value[0], _value[1], false, true), // a <= x < b | |||
| 420: (_value) => IDBKeyRange.bound(_value[0], _value[1], true, true) // a < x < b | |||
| }; | |||
| // 索引的排序方式,正序、倒序 | |||
| // const _description = page.description || 'prev' // 默认倒序 | |||
| /** | |||
| * 遍历查询条件,找到对应的字段,做判断,有一个不符合就返回 false | |||
| * @param {*} other 查询条件 | |||
| * @param {*} model 要核对的对象 | |||
| * @returns 是否符合 | |||
| */ | |||
| const _check = (other, model) => { | |||
| let re = true; | |||
| for (const key in other) { | |||
| const colValue = model[key]; // 被查询的内容 | |||
| const kind = other[key][0]; // 查询方式 | |||
| const _key = other[key][1]; // 查询条件的值 | |||
| const _re = _find[kind](colValue, _key); // 验证 | |||
| if (!_re) re = false; | |||
| } | |||
| return re; | |||
| }; | |||
| /** | |||
| * 处理索引和其他查询 | |||
| * @param {*} indexNames 对象仓库已经设置的索引 | |||
| * @param {*} query 查询条件 | |||
| * @returns IDBKeyRange、其他查询条件、查询方式、回调查询 | |||
| */ | |||
| const _toIndex = (indexNames, query) => { | |||
| const re = { | |||
| range: null, | |||
| other: {}, | |||
| find: (other, model) => _check(other, model), // 查询非索引字段 | |||
| callback: () => {} // 回调查询,可以自定义查询方式 | |||
| }; | |||
| // 匹配到的索引字段 | |||
| let indexName = ""; | |||
| // 查询条件字段和索引字段,匹配一下,只匹配第一个索引字段 | |||
| for (let i = 0; i < indexNames.length; i++) { | |||
| // 索引字段名称 | |||
| const _indexName = indexNames[i]; | |||
| if (typeof query[_indexName] !== "undefined") { | |||
| // 查询方式 | |||
| const _kind = query[_indexName][0]; | |||
| // 查询的值 | |||
| const _value = query[_indexName][1]; | |||
| // 查询条件里包含索引,记录,后面的就不管了。 | |||
| // 匹配查询方式 | |||
| re.range = _dicRange[_kind * 1](_value); | |||
| // 没有的话,说明无法利用索引 | |||
| if (typeof re.range === "undefined") { | |||
| re.range = null; | |||
| } else { | |||
| indexName = _indexName; | |||
| } | |||
| } | |||
| if (indexName !== "") { | |||
| // 退出循环 | |||
| i = indexNames.length; | |||
| } | |||
| } | |||
| // 把其他的查询字段放在 other 里面 | |||
| for (const key in query) { | |||
| const _query = query[key]; | |||
| if (indexName !== key) { | |||
| // 记录到其他查询条件里面 | |||
| re.other[key] = _query; | |||
| } | |||
| } | |||
| return re; | |||
| }; | |||
| export default _toIndex; | |||
| @@ -0,0 +1,19 @@ | |||
| import { unref, isReactive, toRaw } from "vue"; | |||
| // 内部函数,如果是proxy,那么获取原型,否则会报错。 | |||
| const _vueToObject = (obj) => { | |||
| /* | |||
| // 处理 ref | |||
| let _object = unref(obj) | |||
| // 处理 reactive | |||
| if (isReactive(_object)) { | |||
| // 如果是 vue 的 reactive 类型,那么获取原型,否则会报错 | |||
| _object = toRaw(_object) | |||
| } | |||
| */ | |||
| const tmp = unref(obj); | |||
| const _object = isReactive(tmp) ? toRaw(tmp) : tmp; | |||
| return _object; | |||
| }; | |||
| export default _vueToObject; | |||
| @@ -0,0 +1,26 @@ | |||
| /** | |||
| * 初始化数据的时候使用的一个事务。 | |||
| * * help._db 必然可用,不用判断。 | |||
| */ | |||
| const beginInit = (help, storeName) => { | |||
| return new Promise((resolve, reject) => { | |||
| // | |||
| const tranRequest = help._db.transaction(storeName, "readwrite"); | |||
| const store = tranRequest.objectStore(storeName); // 获取store | |||
| tranRequest.onerror = (event) => { | |||
| console.log("读写事务出错:", event.target.error); | |||
| // eslint-disable-next-line prefer-promise-reject-errors | |||
| reject("读写事务出错:" + event.target.error); | |||
| // tranRequest.abort() // 大概是回滚的意思。 | |||
| }; | |||
| tranRequest.oncomplete = (event) => { | |||
| // console.log('初始化数据事务完毕:', window.performance.now(), help._dataState) | |||
| // help._dataState = 'done' | |||
| }; | |||
| resolve(store); | |||
| }); | |||
| }; | |||
| export default beginInit; | |||
| @@ -0,0 +1,32 @@ | |||
| /** | |||
| * 开启一个读写的事务。需要判断 help._db 是否可用 | |||
| * @param {*} help indexedDB 的 help | |||
| * @param {Array} storeName 字符串的数组,对象仓库的名称 | |||
| * @param {string} type readwrite:读写事务;readonly:只读事务;versionchange:允许执行任何操作,包括删除和创建对象存储和索引。 | |||
| * @returns 读写事务 | |||
| */ | |||
| const beginTran = (help, storeName, type = "readwrite") => { | |||
| return new Promise((resolve, reject) => { | |||
| const _tran = () => { | |||
| const tranRequest = help._db.transaction(storeName, type); | |||
| tranRequest.onerror = (event) => { | |||
| const err = `${type} 事务出错:${event.target.error}`; | |||
| console.log(err); | |||
| reject(err); | |||
| }; | |||
| resolve(tranRequest); | |||
| tranRequest.oncomplete = (event) => { | |||
| // console.log('beginReadonly 事务完毕:', window.performance.now()) | |||
| }; | |||
| }; | |||
| if (help._db) { | |||
| _tran(); // 执行事务 | |||
| } else { | |||
| // 注册一个回调事件 | |||
| help._regCallback.push(() => _tran()); | |||
| } | |||
| }); | |||
| }; | |||
| export default beginTran; | |||
| @@ -0,0 +1,270 @@ | |||
| // 加载操作函数 | |||
| // 对象仓库的操作 | |||
| import _clearStore from "./store-clear.js"; // 清空仓库里的全部对象 | |||
| // model 的添加、修改、设置、获取、删除 | |||
| import _addModel from "./model-add.js"; // 添加一个对象 | |||
| import _putModel from "./model-put.js"; // 修改一个对象 | |||
| import _setModel from "./model-set.js"; // 修改一个对象 | |||
| import _getModel from "./model-get.js"; // 获取一个对象,或者全部(不能查询) | |||
| import _delModel from "./model-delete.js"; // 删除一个对象 | |||
| import _getCount from "./model-count.js"; // 获取仓库里的数量 | |||
| // 对象的查询 | |||
| import _listAll from "./list-index.js"; // 获取仓库里符合条件的对象,可以查询。 | |||
| import _listPager from "./list-pager.js"; // 分页获取对象,可以查询 | |||
| // 初始化和事务 | |||
| import _beginInit from "./begin-init.js"; // 初始化时用的事务 | |||
| import _beginTran from "./begin-tran.js"; // 事务 | |||
| /** | |||
| * indexedDB 的 help,基础功能的封装 | |||
| * * 打开数据库,建立对象仓库,获取连接对象,实现增删改查 | |||
| * * info 的结构: | |||
| * * * dbFlag: '' // 数据库标识,区别不同的数据库 | |||
| * * * dbConfig: { // 连接数据库 | |||
| * * * * dbName: '数据库名称', | |||
| * * * * ver: '数据库版本', | |||
| * * * }, | |||
| * * * stores: { | |||
| * * * * storeName: { // 对象仓库名称 | |||
| * * * * * id: 'id', // 主键名称 | |||
| * * * * * index: { // 可以不设置索引 | |||
| * * * * * * name: ture, // key:索引名称;value:是否可以重复 | |||
| * * * * * } | |||
| * * * * } | |||
| * * * }, | |||
| * * * init: (help) => {} // 完全准备好之后的回调函数 | |||
| */ | |||
| export default class IndexedDBHelp { | |||
| constructor(info) { | |||
| this.myIndexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB; | |||
| if (!this.myIndexedDB) { | |||
| console.log("您的浏览器不支持 IndexedDB"); | |||
| return; | |||
| } | |||
| // 数据库名称和版本号 | |||
| this._info = { | |||
| dbName: info.dbConfig.dbName, | |||
| ver: info.dbConfig.ver | |||
| }; | |||
| // 记录连接数据库的对象, IDBDatabase 类型,因为open是异步操作,所以不能立即获得。 | |||
| this._db = null; | |||
| // 记录仓库状态。new:新库或者版本升级后;old:有对象仓库了。 | |||
| this._storeState = "pending"; | |||
| /** | |||
| * 注册回调事件。 | |||
| * * 如果组件读写 indexedDB 的时还没有准备好的话, | |||
| * * 可以来注册一个事件,等准备好了之后回调。 | |||
| */ | |||
| this._regCallback = []; | |||
| // 打开数据库,异步操作,大概需要几毫秒的时间。 | |||
| this.dbRequest = this.myIndexedDB.open(this._info.dbName, this._info.ver); | |||
| // 第一次,或者版本升级时执行,根据配置信息建立表 | |||
| this.dbRequest.onupgradeneeded = (event) => { | |||
| this._storeState = "new"; | |||
| const db = event.target.result; | |||
| for (const key in info.stores) { | |||
| const store = info.stores[key]; | |||
| if (db.objectStoreNames.contains(key)) { | |||
| // 已经有仓库,验证一下是否需要删除原来的仓库 | |||
| if (store.isClear) { | |||
| // 删除原对象仓库,没有保存数据 | |||
| db.deleteObjectStore(key); | |||
| // 建立新对象仓库 | |||
| const objectStore = db.createObjectStore(key, { keyPath: store.id }); | |||
| // 建立索引 | |||
| for (const key2 in store.index) { | |||
| const unique = store.index[key2]; | |||
| objectStore.createIndex(key2, key2, { unique: unique }); | |||
| } | |||
| } | |||
| } else { | |||
| // 没有对象仓库,建立 | |||
| const objectStore = db.createObjectStore(key, { | |||
| keyPath: store.id | |||
| }); /* 自动创建主键 autoIncrement: true */ | |||
| // 建立索引 | |||
| for (const key2 in store.index) { | |||
| const unique = store.index[key2]; | |||
| objectStore.createIndex(key2, key2, { unique: unique }); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| // 数据库打开成功,记录连接对象 | |||
| this.dbRequest.onsuccess = async (event) => { | |||
| this._db = event.target.result; // dbRequest.result | |||
| // console.log('【1】成功打开数据库 onsuccess --- ', this._db) | |||
| // 修改状态 | |||
| if (this._storeState === "pending") { | |||
| this._storeState = "old"; | |||
| } | |||
| // 调用初始化的回调 | |||
| if (typeof info.init === "function") { | |||
| await info.init(this); | |||
| } | |||
| // 调用组件注册的回调 | |||
| this._regCallback.forEach((fn) => { | |||
| if (typeof fn === "function") { | |||
| fn(); | |||
| } | |||
| }); | |||
| }; | |||
| // 处理出错信息 | |||
| this.dbRequest.onerror = (event) => { | |||
| // 出错 | |||
| console.log("打开数据库出错:", event.target.error); | |||
| }; | |||
| } | |||
| // versionchange 全能事务 | |||
| // 初始化时批量添加对象的事务 | |||
| beginInit(storeName) { | |||
| return _beginInit(this, storeName); | |||
| } | |||
| // 读写的事务 | |||
| beginWrite(storeName) { | |||
| return _beginTran(this, storeName, "readwrite"); | |||
| } | |||
| // 只读的事务 | |||
| beginReadonly(storeName) { | |||
| return _beginTran(this, storeName, "readonly"); | |||
| } | |||
| /** | |||
| * 删掉整个库 | |||
| */ | |||
| deleteDB() { | |||
| // 定义一个 Promise 的实例 | |||
| const objectPromise = new Promise((resolve, reject) => { | |||
| // 删掉整个数据库 | |||
| const request = this.myIndexedDB.deleteDatabase(this._info.dbName); | |||
| request.onsuccess = (event) => { | |||
| // 没有触发 | |||
| console.log("删掉整个数据库成功!", event); | |||
| resolve(event); | |||
| }; | |||
| request.onblocked = (event) => { | |||
| // 这个会被触发 | |||
| console.log("删除数据库的 blocked:", event); | |||
| // Close connections here | |||
| resolve(event); | |||
| }; | |||
| request.onerror = (event) => { | |||
| console.log("删除数据库的 error:", event); | |||
| }; | |||
| }); | |||
| return objectPromise; | |||
| } | |||
| /** | |||
| * 清空一个对象仓库的全部对象 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| clearStore(storeName, tran = null) { | |||
| return _clearStore(this, storeName, tran); | |||
| } | |||
| /** | |||
| * 添加一个对象 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {object} model 要添加的对象 | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| addModel(storeName, model, tran = null) { | |||
| return _addModel(this, storeName, model, tran); | |||
| } | |||
| /** | |||
| * 修改一个对象 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {object} model 要修改的对象 | |||
| * @param {number} id 对象主键ID | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| putModel(storeName, model, id = null, tran = null) { | |||
| return _putModel(this, storeName, model, id, tran); | |||
| } | |||
| /** | |||
| * 添加或者修改一个对象 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {object} model 要添加或者修改的对象 | |||
| * @param {number} id 对象主键ID,判断有无的依据 | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| setModel(storeName, model, id = null, tran = null) { | |||
| return _setModel(this, storeName, model, id, tran); | |||
| } | |||
| /** | |||
| * 删除一个对象 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {object} id id 或者 model 要删除的对象 | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| delModel(storeName, id, tran = null) { | |||
| return _delModel(this, storeName, id, tran); | |||
| } | |||
| /** | |||
| * 获取一个对象,或者仓库的全部对象 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {number} id null:获取仓库的全部对象;其他:对象ID值 | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| getModel(storeName, id = null, tran = null) { | |||
| return _getModel(this, storeName, id, tran); | |||
| } | |||
| /** | |||
| * 获取对象仓库里全部对象的数量 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| getCount(storeName, tran = null) { | |||
| return _getCount(this, storeName, tran); | |||
| } | |||
| /** | |||
| * 获取一个仓库的全部对象 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {object} query 查询条件 | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| getList(storeName, query = {}, tran = null) { | |||
| return _listAll(this, storeName, query, tran); | |||
| } | |||
| /** | |||
| * 获取一个仓库的全部对象 | |||
| * @param {string} storeName 对象仓库名称 | |||
| * @param {object} query 查询条件 | |||
| * @param {IDBTransaction} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| listPager(storeName, query = {}, tran = null) { | |||
| return _listPager(this, storeName, query, tran); | |||
| } | |||
| } | |||
| @@ -0,0 +1,146 @@ | |||
| import { reactive } from "vue"; | |||
| // 引入 indexedDB 的help | |||
| import IndexedDB from "./help.js"; | |||
| /** | |||
| * 对 indexedDB 的 help 进行初始化 | |||
| */ | |||
| export default { | |||
| _indexedDBFlag: Symbol("nf-indexedDB-help"), | |||
| _help: {}, // 访问数据库的实例 | |||
| _stores: {}, // 存放对象,实现 foo.addModel(obj)的功能 | |||
| /** | |||
| * 根据参数创建一个数据库的实例,初始化数据库 | |||
| * * 删表、建表、添加默认数据 | |||
| * @param {*} info 参数 | |||
| * @returns | |||
| * * dbFlag: '数据库标识,区分多个数据库', | |||
| * * dbConfig: { // 连接数据库 | |||
| * * * dbName: 'vite2-blog', | |||
| * * * ver: 1.0 | |||
| * * }, | |||
| * * init: () => {}, // 初始化完成后的回调函数 | |||
| * * stores: { | |||
| * * * storeName: { // 对象仓库名 | |||
| * * * * id: 'id', // 主键名称 | |||
| * * * * index: { | |||
| * * * * * name: ture, // 索引:是否可以重复 | |||
| * * * * }, | |||
| * * * * isDeleteOldTable: false, // 是否删除之前的对象仓库 | |||
| * * * } | |||
| * * } | |||
| */ | |||
| createHelp(info) { | |||
| const indexedDBFlag = typeof info.dbFlag === "undefined" ? this._indexedDBFlag : info.dbFlag; | |||
| // 连接数据库,获得实例。 | |||
| const help = new IndexedDB(info); | |||
| const __stores = {}; | |||
| // 存入静态对象,以便于支持保存多个不同的实例。 | |||
| this._help[indexedDBFlag] = help; // help | |||
| this._stores[indexedDBFlag] = __stores; // 仓库变对象 | |||
| help._stores = __stores; // 挂到 help 上面 | |||
| // 把仓库变成对象的形式,避免写字符串的仓库名称 | |||
| for (const key in info.stores) { | |||
| __stores[key] = { | |||
| add: (obj, tran = null) => help.addModel(key, obj, tran), | |||
| get: (id = null, tran = null) => help.getModel(key, id, tran), | |||
| count: (tran = null) => help.getCount(key, tran), | |||
| put: (obj, tran = null) => { | |||
| let _id = obj; | |||
| if (typeof obj === "object") { | |||
| _id = obj[info.stores[key].id]; | |||
| } | |||
| return help.putModel(key, obj, _id, tran); | |||
| }, | |||
| set: (obj, tran = null) => { | |||
| let _id = obj; | |||
| if (typeof obj === "object") { | |||
| _id = obj[info.stores[key].id]; | |||
| } | |||
| return help.setModel(key, obj, _id, tran); | |||
| }, | |||
| del: (obj, tran = null) => { | |||
| let _id = obj; | |||
| if (typeof obj === "object") { | |||
| _id = obj[info.stores[key].id]; | |||
| } | |||
| return help.delModel(key, _id, tran); | |||
| }, | |||
| list: (query = {}, tran = null) => help.getList(key, query, tran), | |||
| begin: () => help.beginWrite(key), | |||
| beginWrite: () => help.beginWrite(key), | |||
| beginReadonly: () => help.beginReadonly(key), | |||
| // 给model 加上增删改查的函数 | |||
| createModel: (model) => { | |||
| class MyModel { | |||
| constructor(_model) { | |||
| for (const key in _model) { | |||
| this[key] = _model[key]; | |||
| } | |||
| } | |||
| /** | |||
| * 添加对象 | |||
| * @param {*} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| add(tran = null) { | |||
| return help.addModel(key, this, tran); | |||
| } | |||
| /** | |||
| * 保存对象,无则添加、有则修改 | |||
| * @param {*} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| save(tran = null) { | |||
| const _id = this[info.stores[key].id]; | |||
| return help.setModel(key, this, _id, tran); | |||
| } | |||
| /** | |||
| * 加载对象,获取对象 | |||
| * @param {*} tran 事务,可以为 null | |||
| */ | |||
| load(tran = null) { | |||
| return new Promise((resolve, reject) => { | |||
| // 套个娃 | |||
| const _id = this[info.stores[key].id]; | |||
| help.getModel(key, _id, tran).then((res) => { | |||
| Object.assign(this, res); | |||
| resolve(res); | |||
| }); | |||
| }); | |||
| } | |||
| /** | |||
| * 删除对象 | |||
| * @param {*} tran 事务,可以为 null | |||
| * @returns | |||
| */ | |||
| del(tran = null) { | |||
| const _id = this[info.stores[key].id]; | |||
| return help.delModel(key, _id, tran); | |||
| } | |||
| } | |||
| const re = new MyModel(model); | |||
| return reactive(re); | |||
| } | |||
| }; | |||
| } | |||
| return help; | |||
| }, | |||
| // 获取静态对象里的数据库实例 | |||
| useDBHelp(_dbFlag) { | |||
| const flag = typeof _dbFlag === "undefined" ? this._indexedDBFlag : _dbFlag; | |||
| return this._help[flag]; | |||
| }, | |||
| useStores(_dbFlag) { | |||
| const flag = typeof _dbFlag === "undefined" ? this._indexedDBFlag : _dbFlag; | |||
| return this._stores[flag]; | |||
| } | |||
| }; | |||
| @@ -0,0 +1,106 @@ | |||
| /** | |||
| * 不分页获取数据,可以查询 | |||
| * @param { dbHelp } help 访问数据库的实例 | |||
| * @param { Object } storeName 对象仓库 | |||
| * @param { Object } findInfo 查询条件 | |||
| * @param { Object } pager 排序字段 | |||
| * @returns 添加记录的ID | |||
| * * findInfo 结构(查询条件): | |||
| * * * { colName: [401, 11] } 字段名称,查询方式,查询关键字 | |||
| * * pager 结构: | |||
| * * * orderBy: { id: false } // 排序字段:字段名,false表示倒序。 | |||
| */ | |||
| const getList = (help, storeName, findInfo = {}, pager = {}) => { | |||
| const _start = page.start || 0; | |||
| const _count = page.count || 0; | |||
| const _end = _start + _count; | |||
| const _description = pager.description || "prev"; // 默认倒序 | |||
| // 查询条件,按照主键或者索引查询 | |||
| let keyRange = null; | |||
| if (typeof findInfo.indexName !== "undefined") { | |||
| if (typeof findInfo.indexKind !== "undefined") { | |||
| const id = findInfo.indexValue; | |||
| const dicRange = { | |||
| "=": IDBKeyRange.only(id), | |||
| ">": IDBKeyRange.lowerBound(id, true), | |||
| ">=": IDBKeyRange.lowerBound(id), | |||
| "<": IDBKeyRange.upperBound(id, true), | |||
| "<=": IDBKeyRange.upperBound(id) | |||
| }; | |||
| const betweenInfo = findInfo.betweenInfo; | |||
| switch (findInfo.indexKind) { | |||
| case "=": | |||
| case ">": | |||
| case ">=": | |||
| case "<": | |||
| case "<=": | |||
| keyRange = dicRange[findInfo.indexKind]; | |||
| break; | |||
| case "between": | |||
| keyRange = IDBKeyRange.bound( | |||
| betweenInfo.v1, | |||
| betweenInfo.v2, | |||
| betweenInfo.v1isClose, | |||
| betweenInfo.v2isClose | |||
| ); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| console.log("findObject - keyRange", keyRange); | |||
| return new Promise((resolve, reject) => { | |||
| const _getList = (__tran) => { | |||
| const store = tranRequest.objectStore(storeName); | |||
| let cursorRequest; | |||
| // 打开游标 | |||
| cursorRequest = store.openCursor(keyRange, _description); | |||
| // 使用游标查询对象并且返回 | |||
| cursorRequest.onsuccess = (event) => { | |||
| const cursor = event.target.result; | |||
| if (cursor) { | |||
| if (_end === 0 || (cursorIndex >= _start && cursorIndex < _end)) { | |||
| // 判断钩子函数 | |||
| if (typeof findInfo.where === "function") { | |||
| if (findInfo.where(cursor.value, cursorIndex)) { | |||
| dataList.push(cursor.value); | |||
| cursorIndex++; | |||
| } | |||
| } else { | |||
| // 没有设置查询条件 | |||
| dataList.push(cursor.value); | |||
| cursorIndex++; | |||
| } | |||
| } | |||
| cursor.continue(); | |||
| } | |||
| // tranRequest.commit() | |||
| }; | |||
| tranRequest.oncomplete = (event) => { | |||
| if (config.debug) { | |||
| console.log("findObjectByIndex - dataList", dataList); | |||
| } | |||
| resolve(dataList); | |||
| }; | |||
| tranRequest.onerror = (event) => { | |||
| console.log("findObjectByIndex - onerror", event); | |||
| reject(event); | |||
| }; | |||
| }; | |||
| // 判断数据库是否打开 | |||
| if (tranRequest === null) { | |||
| // 自己开一个事务 | |||
| help.beginReadonly([storeName]).then((tran) => { | |||
| _getList(tran); | |||
| tran.commit(); // 可以快点提交事务,好吧其实也没快。 | |||
| }); | |||
| } else { | |||
| _getList(tranRequest); | |||
| } | |||
| }); | |||
| }; | |||
| export default getList; | |||
| @@ -0,0 +1,60 @@ | |||
| import _toIndex from "./_toIndex.js"; | |||
| /** | |||
| * 用索引查询数据 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { Object } query 查询条件 | |||
| * * query: { | |||
| * * * keyName1: [401, xxx], // 第一个是索引, | |||
| * * * keyName2: [402, xxx] // 后面的不算索引 | |||
| * * } | |||
| * @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 要获取的对象 | |||
| */ | |||
| const getList = (help, storeName, query = {}, tranRequest = null) => { | |||
| return new Promise((resolve, reject) => { | |||
| // 定义个函数,便于调用 | |||
| const _getList = (__tran) => { | |||
| const store = __tran.objectStore(storeName); | |||
| // 分析查询条件,设置索引和其他条件 | |||
| const { range, other, find } = _toIndex(store.indexNames, query); | |||
| const keys = Object.keys(query); | |||
| // 设置索引的查询条件 | |||
| // 打开对象仓库或者索引 ? 不用索引,直接用对象仓库 : 使用索引 | |||
| const dbRequest = range === null ? store : store.index(keys[0]); | |||
| // // 直接开游标 : // 带条件的开游标 | |||
| const cursor = range === null ? dbRequest.openCursor() : dbRequest.openCursor(range); | |||
| const arr = []; // 返回的记录集 | |||
| let i = 0; | |||
| cursor.onsuccess = (event) => { | |||
| // 打开成功 | |||
| const res = event.target.result; | |||
| console.log("游标:" + i++, res); | |||
| if (res) { | |||
| // 判断其他查询条件 | |||
| if (find(other, res.value)) { | |||
| arr.push(res.value); | |||
| } | |||
| res.continue(); // 继续下一个 | |||
| } else { | |||
| // 没有了 | |||
| resolve(arr); // 返回对象 | |||
| } | |||
| }; | |||
| }; | |||
| // 判断数据库是否打开 | |||
| if (tranRequest === null) { | |||
| // 自己开一个事务 | |||
| help.beginReadonly([storeName]).then((tran) => { | |||
| _getList(tran); | |||
| // tran.commit() // 可以快点提交事务,好吧其实也没快。 | |||
| }); | |||
| } else { | |||
| _getList(tranRequest); | |||
| } | |||
| }); | |||
| }; | |||
| export default getList; | |||
| @@ -0,0 +1,124 @@ | |||
| /** | |||
| * 获取分页列表数据 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { Object } model 对象(数据记录) | |||
| * @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * storeName: '', 对象仓库名 | |||
| * * info 结构: | |||
| * * * find: { | |||
| * * * * indexName: 'groupId', | |||
| * * * * indexKind: '=', // '>','>=','<','<=','between', | |||
| * * * }, | |||
| * * * pager: { | |||
| * * * * start:开始, | |||
| * * * * count:数量, | |||
| * * * * description:'next' | |||
| * * * * indexValue: 1, | |||
| * * * * betweenInfo: { | |||
| * * * * * v1:1, | |||
| * * * * * v2:2, | |||
| * * * * * v1isClose:true, | |||
| * * * * * v2isClose:true, | |||
| * * * * }, | |||
| * * * * where:(object) => { | |||
| * * * * * reutrn true/false | |||
| * * * * } | |||
| * * * } | |||
| */ | |||
| export default function pager(help, storeName, info, tranRequest = null) { | |||
| const _start = info.pager.start || 0; | |||
| const _count = info.pager.count || 0; | |||
| const _end = _start + _count; | |||
| const _description = info.pager.description || "prev"; // 默认倒序 | |||
| // 查询条件,按照主键或者索引查询 | |||
| let keyRange = null; | |||
| if (typeof info.find.indexName !== "undefined") { | |||
| if (typeof info.find.indexKind !== "undefined") { | |||
| const id = info.find.indexValue; | |||
| const dicRange = { | |||
| "=": IDBKeyRange.only(id), | |||
| ">": IDBKeyRange.lowerBound(id, true), | |||
| ">=": IDBKeyRange.lowerBound(id), | |||
| "<": IDBKeyRange.upperBound(id, true), | |||
| "<=": IDBKeyRange.upperBound(id) | |||
| }; | |||
| const betweenInfo = info.find.betweenInfo || "=="; | |||
| switch (findInfo.indexKind) { | |||
| case "=": | |||
| case ">": | |||
| case ">=": | |||
| case "<": | |||
| case "<=": | |||
| keyRange = dicRange[findInfo.indexKind]; | |||
| break; | |||
| case "between": | |||
| keyRange = IDBKeyRange.bound( | |||
| betweenInfo.v1, | |||
| betweenInfo.v2, | |||
| betweenInfo.v1isClose, | |||
| betweenInfo.v2isClose | |||
| ); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| console.log("pager - keyRange", keyRange); | |||
| // 定义一个 Promise 的实例 | |||
| return new Promise((resolve, reject) => { | |||
| const dataList = []; | |||
| // 定义个函数,便于调用 | |||
| const _pager = (__tran) => { | |||
| let cursorIndex = 0; | |||
| const store = __tran.objectStore(storeName); | |||
| let cursorRequest; | |||
| // 判断是否索引查询 | |||
| if (typeof findInfo.indexName === "undefined") { | |||
| cursorRequest = store.openCursor(keyRange, _description); | |||
| } else { | |||
| cursorRequest = store.index(info.find.indexName).openCursor(keyRange, _description); | |||
| } | |||
| cursorRequest.onsuccess = (event) => { | |||
| const cursor = event.target.result; | |||
| if (cursor) { | |||
| if (_end === 0 || (cursorIndex >= _start && cursorIndex < _end)) { | |||
| // 判断钩子函数 | |||
| if (typeof findInfo.where === "function") { | |||
| if (findInfo.where(cursor.value, cursorIndex)) { | |||
| dataList.push(cursor.value); | |||
| cursorIndex++; | |||
| } | |||
| } else { | |||
| // 没有设置查询条件 | |||
| dataList.push(cursor.value); | |||
| cursorIndex++; | |||
| } | |||
| } | |||
| cursor.continue(); | |||
| } | |||
| // __tran.commit() | |||
| }; | |||
| __tran.oncomplete = (event) => { | |||
| if (config.debug) { | |||
| console.log("oncomplete - pager - dataList", dataList); | |||
| } | |||
| resolve(dataList); | |||
| }; | |||
| }; | |||
| if (tranRequest === null) { | |||
| help.beginReadonly([storeName]).then((tran) => { | |||
| // 自己开一个事务 | |||
| _pager(tran); | |||
| }); | |||
| } else { | |||
| // 使用传递过来的事务 | |||
| _pager(tranRequest); | |||
| } | |||
| }); | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| import _vueToObject from "./_toObject.js"; | |||
| /** | |||
| * 添加对象 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { Object } model 对象(数据记录) | |||
| * @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 新对象的ID | |||
| */ | |||
| export default function addModel(help, storeName, model, tranRequest = null) { | |||
| console.log("添加对象的 this", this); | |||
| // 取对象的原型,便于保存 reactive | |||
| const _model = _vueToObject(model); | |||
| // 定义一个 Promise 的实例 | |||
| return new Promise((resolve, reject) => { | |||
| // 定义个函数,便于调用 | |||
| const _add = (__tran) => { | |||
| __tran | |||
| .objectStore(storeName) // 获取store | |||
| .add(_model).onsuccess = (event) => { | |||
| // 添加对象 | |||
| // 成功后的回调 | |||
| resolve(event.target.result); // 返回对象的ID | |||
| }; | |||
| }; | |||
| if (tranRequest === null) { | |||
| help.beginWrite([storeName]).then((tran) => { | |||
| // 自己开一个事务 | |||
| _add(tran); | |||
| }); | |||
| } else { | |||
| // 使用传递过来的事务 | |||
| _add(tranRequest); | |||
| } | |||
| }); | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| /** | |||
| * 获取对象 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { IDBTransaction } tran 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 要获取的对象 | |||
| */ | |||
| export default function getCount(help, storeName, tran = null) { | |||
| return new Promise((resolve, reject) => { | |||
| // 定义个函数,便于调用 | |||
| const _getCount = (__tran) => { | |||
| const store = __tran.objectStore(storeName); | |||
| // console.log('对象仓库--', store) | |||
| // 判断是获取一个,还是获取全部 | |||
| const dbRequest = store.count(); | |||
| // console.log('dbRequest--', dbRequest) | |||
| dbRequest.onsuccess = (event) => { | |||
| // 成功后的回调 | |||
| // console.log('-- 得到数据 --:', window.performance.now()) | |||
| resolve(event.target.result); // 返回对象 | |||
| }; | |||
| }; | |||
| // 判断数据库是否打开 | |||
| if (tran === null) { | |||
| // 自己开一个事务 | |||
| help.beginReadonly([storeName]).then((tran) => { | |||
| _getCount(tran); | |||
| tran.commit(); // 可以快点提交事务,好吧其实也没快。 | |||
| }); | |||
| } else { | |||
| _getCount(tran); | |||
| } | |||
| }); | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| /** | |||
| * 删除对象 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { string } id 对象的ID | |||
| * @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| */ | |||
| export default function deleteData(help, storeName, id, tranRequest = null) { | |||
| // 定义一个 Promise 的实例 | |||
| return new Promise((resolve, reject) => { | |||
| // 定义个函数,便于调用 | |||
| const _delete = (__tran) => { | |||
| __tran | |||
| .objectStore(storeName) // 获取store | |||
| .delete(id).onsuccess = (event) => { | |||
| // 删除一个对象 | |||
| // 成功后的回调 | |||
| resolve(event.target.result); | |||
| }; | |||
| }; | |||
| // 判断是否有事务 | |||
| if (tranRequest === null) { | |||
| help.beginWrite([storeName]).then((tran) => { | |||
| _delete(tran); | |||
| }); | |||
| } else { | |||
| _delete(tranRequest); | |||
| } | |||
| }); | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| /** | |||
| * 获取对象 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { Object } id 对象(提供id) | |||
| * @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 要获取的对象 | |||
| */ | |||
| export default function getModel(help, storeName, id = null, tranRequest = null) { | |||
| return new Promise((resolve, reject) => { | |||
| // 定义个函数,便于调用 | |||
| const _getModel = (__tran) => { | |||
| const store = __tran.objectStore(storeName); | |||
| // console.log('对象仓库--', store) | |||
| // 判断是获取一个,还是获取全部 | |||
| const dbRequest = id === null ? store.getAll() : store.get(id); | |||
| // console.log('dbRequest--', dbRequest) | |||
| dbRequest.onsuccess = (event) => { | |||
| // 成功后的回调 | |||
| // console.log('-- 得到数据 --:', window.performance.now()) | |||
| resolve(event.target.result); // 返回对象 | |||
| }; | |||
| }; | |||
| // 判断数据库是否打开 | |||
| if (tranRequest === null) { | |||
| // 自己开一个事务 | |||
| help.beginReadonly([storeName]).then((tran) => { | |||
| _getModel(tran); | |||
| tran.commit(); // 可以快点提交事务,好吧其实也没快。 | |||
| }); | |||
| } else { | |||
| _getModel(tranRequest); | |||
| } | |||
| }); | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| import _vueToObject from "./_toObject.js"; | |||
| /** | |||
| * 修改对象,先依据ID获取对象,然后把model的属性叠加上去,最后put新对象 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { Object } model 对象(数据记录) | |||
| * @param { string } id 对象的ID | |||
| * @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| */ | |||
| export default function updateData(help, storeName, model, id, tranRequest = null) { | |||
| const _model = _vueToObject(model); | |||
| // 定义一个 Promise 的实例 | |||
| return new Promise((resolve, reject) => { | |||
| // 定义个函数,便于调用 | |||
| const _update = (__tran) => { | |||
| // 先获取对象,然后修改对象,最后存回去 | |||
| const store = __tran.objectStore(storeName); // 获取store | |||
| store.get(id).onsuccess = (event) => { | |||
| // 获取对象 | |||
| // 成功后的回调 | |||
| // 从仓库里提取对象,把修改值合并到对象里面。 | |||
| const newObject = {}; | |||
| Object.assign(newObject, event.target.result, _model); | |||
| // 修改数据 | |||
| store.put(newObject).onsuccess = (event) => { | |||
| // 修改对象 | |||
| // 成功后的回调 | |||
| resolve(event.target.result); | |||
| }; | |||
| }; | |||
| }; | |||
| // 判断是否自带事务 | |||
| if (tranRequest === null) { | |||
| help.beginWrite([storeName]).then((tran) => { | |||
| // 自己开一个事务 | |||
| _update(tran); | |||
| }); | |||
| } else { | |||
| // 使用传递过来的事务 | |||
| _update(tranRequest); | |||
| } | |||
| }); | |||
| } | |||
| @@ -0,0 +1,56 @@ | |||
| import _vueToObject from "./_toObject.js"; | |||
| /** | |||
| * 添加或者修改对象,先依据ID判断是否有对象,无则添,有则改 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { Object } model 对象(数据记录) | |||
| * @param { string } id 对象的ID | |||
| * @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| */ | |||
| export default function setData(help, storeName, model, id, tranRequest = null) { | |||
| const _model = _vueToObject(model); | |||
| // 定义一个 Promise 的实例 | |||
| return new Promise((resolve, reject) => { | |||
| // 定义个函数,便于调用 | |||
| const _set = (__tran) => { | |||
| // 先获取对象,然后修改对象,最后存回去 | |||
| const store = __tran.objectStore(storeName); // 获取store | |||
| store.get(id).onsuccess = (event) => { | |||
| // 获取对象 | |||
| // 成功后的回调 | |||
| // 从仓库里提取对象,把修改值合并到对象里面。 | |||
| const res = event.target.result; | |||
| if (typeof res === "undefined") { | |||
| // 没有对象添加 | |||
| store.add(_model).onsuccess = (event) => { | |||
| // 添加对象 | |||
| // 成功后的回调 | |||
| resolve(event.target.result); // 返回对象的ID | |||
| }; | |||
| } else { | |||
| // 修改 | |||
| const newObject = {}; | |||
| Object.assign(newObject, event.target.result, _model); | |||
| // 修改数据 | |||
| store.put(newObject).onsuccess = (event) => { | |||
| // 修改对象 | |||
| // 成功后的回调 | |||
| resolve(event.target.result); | |||
| }; | |||
| } | |||
| }; | |||
| }; | |||
| // 判断是否自带事务 | |||
| if (tranRequest === null) { | |||
| help.beginWrite([storeName]).then((tran) => { | |||
| // 自己开一个事务 | |||
| _set(tran); | |||
| }); | |||
| } else { | |||
| // 使用传递过来的事务 | |||
| _set(tranRequest); | |||
| } | |||
| }); | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| /** | |||
| * 清空仓库 store 里的对象 | |||
| * @param { IndexedDBHelp } help 访问数据库的实例 | |||
| * @param { string } storeName 仓库名称(表名) | |||
| * @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns | |||
| */ | |||
| export default function deleteStore(help, storeName, tranRequest = null) { | |||
| // 定义一个 Promise 的实例 | |||
| return new Promise((resolve, reject) => { | |||
| // 定义个函数,便于调用 | |||
| const _clearStore = (__tran) => { | |||
| __tran | |||
| .objectStore(storeName) // 获取store | |||
| .clear().onsuccess = (event) => { | |||
| // 删除store | |||
| // 成功后的回调 | |||
| resolve(event); // 返回对象的ID | |||
| }; | |||
| }; | |||
| if (tranRequest === null) { | |||
| help.beginWrite([storeName]).then((tran) => { | |||
| // 自己开一个事务 | |||
| _clearStore(tran); | |||
| }); | |||
| } else { | |||
| // 使用传递过来的事务 | |||
| _clearStore(tranRequest); | |||
| } | |||
| }); | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| /** | |||
| * 内部函数,根据分页信息,设置 分页信息和排序字段 | |||
| * @param { object } pager 分页和排序 | |||
| * @returns order by 和 limit | |||
| */ | |||
| const _getPager = (pager) => { | |||
| const re = { | |||
| orderBy: "", // 分页,可以不设置 | |||
| limit: "" // 排序,可以不设置 | |||
| }; | |||
| // 设置分页 order by 和 limit | |||
| if (typeof pager !== "undefined") { | |||
| if (typeof pager.pagerIndex !== "undefined") { | |||
| // 用 limit 实现分页 | |||
| const _pageSize = pager.pagerSize || 10; | |||
| const index = _pageSize * (pager.pagerIndex - 1); | |||
| re.limit = ` limit ${index}, ${_pageSize} `; | |||
| } | |||
| if (typeof pager.orderBy === "object") { | |||
| // 设置排序字段和方式 | |||
| const arr = []; | |||
| for (const key in pager.orderBy) { | |||
| const col = key; | |||
| const isAsc = pager.orderBy[key] ? " asc " : " desc "; | |||
| arr.push(col + " " + isAsc); | |||
| } | |||
| re.orderBy = ` order by ${arr.join(",")}`; | |||
| } | |||
| } | |||
| return re; | |||
| }; | |||
| export default _getPager; | |||
| @@ -0,0 +1,81 @@ | |||
| /** | |||
| * 内部函数,根据查询条件,拼接查询用的SQL语句,where 后面的部分。 | |||
| * @param {object} query 查询条件 | |||
| * @returns where 后面的查询语句 | |||
| */ | |||
| const _getWhereQuery = (query) => { | |||
| // 查询条件 | |||
| const findKind = { | |||
| // 字符串 | |||
| 401: " {col} = ? ", | |||
| 402: " {col} <> ? ", | |||
| 403: " {col} like ? ", | |||
| 404: " {col} not like ? ", | |||
| 405: " {col} like ? ", // 起始于 | |||
| 406: " {col} like ? ", // 结束于 | |||
| // 数字 | |||
| 411: " {col} = ? ", | |||
| 412: " {col} <> ? ", | |||
| 413: " {col} > ? ", | |||
| 414: " {col} >= ? ", | |||
| 415: " {col} < ? ", | |||
| 416: " {col} <= ? ", | |||
| 417: " {col} between ? and ? ", | |||
| // 日期 | |||
| 421: " {col} = ? ", | |||
| 422: " {col} <> ? ", | |||
| 423: " {col} > ? ", | |||
| 424: " {col} >= ? ", | |||
| 425: " {col} < ? ", | |||
| 426: " {col} <= ? ", | |||
| 427: " {col} between ? and ? ", | |||
| // 范围 | |||
| 441: " {col} in (?)" | |||
| }; | |||
| const _whereCol = []; // 查询字段 | |||
| const _whereValue = []; // 查询参数 | |||
| // 设置查询条件 | |||
| for (const key in query) { | |||
| const val = query[key]; | |||
| if (val[1] === "" || val[1] === null) continue; | |||
| _whereCol.push(findKind[val[0]].replace("{col}", key)); | |||
| switch (val[0]) { | |||
| case 403: // like | |||
| case 404: // not like | |||
| _whereValue.push("%" + val[1] + "%"); | |||
| break; | |||
| case 405: // like a% | |||
| _whereValue.push(val[1] + "%"); | |||
| break; | |||
| case 406: // like %a | |||
| _whereValue.push("%" + val[1]); | |||
| break; | |||
| case 417: // between 数字 | |||
| case 427: // between 日期 | |||
| _whereValue.push(...val[1]); | |||
| break; | |||
| case 441: // in | |||
| _whereCol[_whereCol.length - 1] = _whereCol[_whereCol.length - 1].replace( | |||
| "?", | |||
| val[1].map((a) => "?").join(",") | |||
| ); | |||
| _whereValue.push(...val[1]); | |||
| break; | |||
| default: | |||
| _whereValue.push(val[1]); | |||
| break; | |||
| } | |||
| } | |||
| const re = { | |||
| whereQuery: "", | |||
| whereValue: [] | |||
| }; | |||
| // 如果没有查询添加,设置 1=1 占位 | |||
| if (_whereCol.length > 0) { | |||
| (re.whereQuery = ` WHERE ${_whereCol.join(" and ")}`), (re.whereValue = _whereValue); | |||
| } | |||
| return re; | |||
| }; | |||
| export default _getWhereQuery; | |||
| @@ -0,0 +1,52 @@ | |||
| /** | |||
| * 实现添加数据的功能。拼接 insert 的 SQL语句 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { Object } model 数据 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * cols: { colName: '', ...} | |||
| * * model 结构: | |||
| * * * colName: value | |||
| */ | |||
| export default function addData(help, meta, model, cn = null) { | |||
| // 拼接添加用的SQL语句, | |||
| // 提交SQL语句 | |||
| // console.log('addData,开始运行 :') | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| // 记录字段名称 | |||
| const colNames = []; | |||
| // 记录字段对应的值 | |||
| const colValues = []; | |||
| // 记录字段对应的占位符合 | |||
| const cols = []; | |||
| // 变量对象,记录 key和 value | |||
| const colKeys = meta.cols || model; | |||
| for (const key in colKeys) { | |||
| if (key.toLocaleLowerCase() === "id") continue; | |||
| colNames.push(key); | |||
| // 判断类型 | |||
| if (typeof model[key] === "object") { | |||
| colValues.push(JSON.stringify(model[key])); | |||
| } else { | |||
| colValues.push(model[key]); | |||
| } | |||
| cols.push("?"); | |||
| } | |||
| const sql = `INSERT INTO ${meta.tableName} ( ${colNames.join(",")} ) VALUES ( ${cols.join(",")} )`; | |||
| // console.log('insertSQL:', sql) | |||
| help.query(sql, colValues, cn) | |||
| .then((res) => { | |||
| // 成功了,返回给调用者 | |||
| resolve(res.insertId); | |||
| }) | |||
| .catch((err) => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /** | |||
| * 实现删除数据的功能。物理删除,delete from | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { number|string } id 主键字段值 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| */ | |||
| export default function deleteData(help, meta, id, cn = null) { | |||
| // 拼接添加用的SQL语句, | |||
| // 提交SQL语句 | |||
| // console.log('deleteData,开始运行 :') | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| const sql = `DELETE FROM ${meta.tableName} WHERE ${meta.idKey}=?`; | |||
| console.log("sql-----delete:", sql, id); | |||
| help.query(sql, [id], cn) | |||
| .then((res) => { | |||
| // 成功了,返回给调用者 | |||
| resolve(res.rowsAffected); | |||
| }) | |||
| .catch((err) => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| /** | |||
| * 实现删除数据的功能。逻辑删除,update set flag = 1 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { number|string } id 主键字段值 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| * * * delFlag: 'isDel', 逻辑删除,标记字段名称 | |||
| */ | |||
| export default function deleteData(help, meta, id, cn = null) { | |||
| // 拼接添加用的SQL语句, | |||
| // 提交SQL语句 | |||
| // console.log('deleteData,开始运行 :') | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| const sql = `UPDATE ${meta.tableName} SET ${meta.delFlag} = 1 WHERE ${meta.idKey}=?`; | |||
| // console.log('sql:', sql) | |||
| help.query(sql, [id], cn) | |||
| .then((res) => { | |||
| // 成功了,返回给调用者 | |||
| resolve(res.rowsAffected); | |||
| }) | |||
| .catch((err) => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /** | |||
| * 依据主键字段,获取记录 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { number|string } id 主键字段值 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| */ | |||
| export default function getData(help, meta, id, cn = null) { | |||
| // 拼接添加用的SQL语句, | |||
| // 提交SQL语句 | |||
| // console.log('getData,开始运行 :') | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| const sql = `SELECT * FROM ${meta.tableName} WHERE ${meta.idKey || "id"}=?`; | |||
| // console.log('SELECT -- sql:', sql, id) | |||
| help.query(sql, [id], cn) | |||
| .then((res) => { | |||
| // 成功了,返回给调用者 | |||
| resolve(res.rows); | |||
| }) | |||
| .catch((err) => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| import add from "./data-add"; | |||
| import update from "./data-update"; | |||
| /** | |||
| * 实现添加/修改数据的功能。没有ID:添加,有ID:修改 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { Object } model 数据 | |||
| * @param { number|string } id 主键字段值 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 影响的行数 | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| * * model 结构: | |||
| * * * colName: value | |||
| */ | |||
| export default function updateData(help, meta, model, id, cn = null) { | |||
| // 拼接修改用的SQL语句, | |||
| // console.log('updateData,开始运行 :') | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| // 记录字段名称 | |||
| const colNames = []; | |||
| // 记录字段对应的值 | |||
| const colValues = []; | |||
| // 变量对象,记录 key和 value | |||
| const colKeys = meta.cols || model; | |||
| for (const key in colKeys) { | |||
| colNames.push(key + "=? "); | |||
| if (typeof model[key] === "object") { | |||
| colValues.push(JSON.stringify(model[key], null, 2)); | |||
| } else { | |||
| colValues.push(model[key]); | |||
| } | |||
| } | |||
| // 加入查询条件 | |||
| colValues.push(id); | |||
| const sql = `SELECT 1 FROM ${meta.tableName} WHERE ${meta.idKey || "id"}=?`; | |||
| // console.log('updateSQL:', sql) | |||
| help.query(sql, colValues, cn) | |||
| .then((res) => { | |||
| add(help, meta, model, id, cn).then((res1) => { | |||
| // 成功了,返回给调用者 | |||
| resolve(res.rowsAffected); | |||
| }); | |||
| }) | |||
| .catch((err) => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| /** | |||
| * 实现修改数据的功能。拼接 update 的 SQL语句 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { Object } model 数据 | |||
| * @param { number|string } id 主键字段值 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 影响的行数 | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| * * model 结构: | |||
| * * * colName: value | |||
| */ | |||
| export default function updateData(help, meta, model, id, cn = null) { | |||
| // 拼接修改用的SQL语句, | |||
| // console.log('updateData,开始运行 :') | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| // 记录字段名称 | |||
| const colNames = []; | |||
| // 记录字段对应的值 | |||
| const colValues = []; | |||
| // 变量对象,记录 key和 value | |||
| const colKeys = meta.cols || model; | |||
| for (const key in colKeys) { | |||
| colNames.push(key + "=? "); | |||
| if (typeof model[key] === "object") { | |||
| colValues.push(JSON.stringify(model[key], null, 2)); | |||
| } else { | |||
| colValues.push(model[key]); | |||
| } | |||
| } | |||
| // 加入查询条件 | |||
| colValues.push(id); | |||
| const sql = `UPDATE ${meta.tableName} SET ${colNames.join(",")} WHERE ${meta.idKey}=?`; | |||
| // console.log('updateSQL:', sql) | |||
| help.query(sql, colValues, cn) | |||
| .then((res) => { | |||
| // 成功了,返回给调用者 | |||
| resolve(res.rowsAffected); | |||
| }) | |||
| .catch((err) => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| /** | |||
| * 删除数据库 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { string } dbName 数据库名称 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| * * * delFlag: 'isDel', 逻辑删除,标记字段名称 | |||
| */ | |||
| export default function deleteTable(help, dbName, cn = null) { | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| const sql = `DROP database ${dbName} `; | |||
| // console.log('sql:', sql) | |||
| help.query(sql, [], cn) | |||
| .then((res) => { | |||
| // 成功了,返回给调用者 | |||
| resolve(res); | |||
| }) | |||
| .catch((err) => { | |||
| console.log("deleteDataBase-sql:", sql, err); | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,211 @@ | |||
| import _addModel from "./data-add.js"; // 添加一个对象 | |||
| import _updateModel from "./data-update.js"; // 修改一个对象 | |||
| import _getModel from "./data-get.js"; // 获取一个对象 | |||
| import _deleteModel from "./data-delete.js"; // 删除一个对象 | |||
| import _listAll from "./list-all.js"; // 获取仓库里全部对象 | |||
| import _listCount from "./list-count.js"; // 统计对象数量 | |||
| import _listPager from "./list-pager.js"; // 分页获取对象 | |||
| /** | |||
| * 基于 promise 封装 webSQL 的基本操作 | |||
| * * 创建数据库连接 | |||
| * * 提交SQL给数据库执行,得到返回结果 | |||
| * * 共用函数 | |||
| * * info 结构: | |||
| * * * dbName: 'test', // 数据库名称 | |||
| * * * ver: '1', // 版本,很重要 | |||
| * * * size: '2', // 大小,自动 * 1024 * 1024 | |||
| * * * description: '数据库描述' | |||
| */ | |||
| export default class MySQLHelp { | |||
| constructor(info) { | |||
| if (!window.openDatabase) { | |||
| console.log("您的浏览器不支持 webSQL"); | |||
| return; | |||
| } | |||
| // 数据库连接信息 | |||
| this._info = { | |||
| dbName: info.dbName, | |||
| ver: info.ver, | |||
| size: info.size, | |||
| description: info.infodescription | |||
| }; | |||
| // 打开数据库 | |||
| this.db = window.openDatabase( | |||
| this._info.dbName, | |||
| this._info.ver, | |||
| this._info.description, | |||
| this._info.size * 1024 * 1024 | |||
| ); | |||
| // console.log('\n db', this.db.version) | |||
| } | |||
| /** | |||
| * 运行 SQL 语句,带参数,返回执行结果 | |||
| * @param { string } sql SQL语句 | |||
| * @param { array } param 参数 | |||
| * @param { object } tran 参数 | |||
| * @returns promise | |||
| */ | |||
| query(sql, param = [], tran = null) { | |||
| const promise1 = new Promise((resolve, reject) => { | |||
| const _query = (tran) => { | |||
| tran.executeSql( | |||
| sql, | |||
| param, | |||
| (tx, results) => { | |||
| resolve(results); | |||
| }, | |||
| (tx, err) => { | |||
| console.log("query - sql:", sql, param, tx, err); | |||
| reject(err); | |||
| return true; // 回滚 | |||
| } | |||
| ); | |||
| }; | |||
| if (tran === null) { | |||
| this.begin().then((_tran) => { | |||
| _query(_tran); | |||
| }); | |||
| } else { | |||
| _query(tran); | |||
| } | |||
| }); | |||
| return promise1; | |||
| } | |||
| /** | |||
| * 开启一个事务,Promise 的方式 | |||
| * @returns Promise 形式 | |||
| */ | |||
| begin() { | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| // 开启一个事务。 | |||
| // console.log('★ 开启事务,promise 模式') | |||
| this.db.transaction( | |||
| (tran) => { | |||
| resolve(tran); | |||
| }, | |||
| (tx, err) => { | |||
| reject(err); | |||
| } | |||
| ); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| /** | |||
| * 提交一个事务 | |||
| * @param { connection } tran 开启事务时创建的连接对象 | |||
| * @returns 提交事务 | |||
| */ | |||
| commit(tran) { | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| // 提交事务 | |||
| tran.commit((err) => { | |||
| if (err) { | |||
| console.log("事务提交失败", err); | |||
| reject(err); | |||
| } else { | |||
| resolve(); | |||
| } | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| /** | |||
| * 关闭数据库 | |||
| * @param { connection } tran 开启事务时创建的连接对象 | |||
| */ | |||
| close(tran = null) { | |||
| if (tran !== null) { | |||
| // 归还连接对象。console.log('--close: tran', tran.threadId) | |||
| tran.release(); | |||
| // console.log('\n[MySQL 事务,已经关闭数据库:] \n') | |||
| } else { | |||
| // 关闭连接 | |||
| this.db.end((err) => { | |||
| if (err) { | |||
| console.error("关闭连接发生错误:", err); | |||
| } else { | |||
| // console.log('\n[MySQL 已经关闭数据库:]\n') | |||
| } | |||
| }); | |||
| } | |||
| } | |||
| /** | |||
| * 添加一条记录 | |||
| * @param {string} meta 表名 | |||
| * @param {object} model 要添加的记录 | |||
| * @param {*} tran 事务,可以为null | |||
| * @returns | |||
| */ | |||
| addModel(meta, model, tran = null) { | |||
| return _addModel(this, meta, model, tran); | |||
| } | |||
| /** | |||
| * 修改一条记录 | |||
| * @param {string} meta 表名 | |||
| * @param {object} model 要添加的记录 | |||
| * @param {*} tran 事务,可以为null | |||
| * @returns | |||
| */ | |||
| updateModel(meta, model, tran = null) { | |||
| return _updateModel(this, meta, model, tran); | |||
| } | |||
| /** | |||
| * 删除一条记录 | |||
| * @param {string} tableName 表名 | |||
| * @param {object} model 要添加的记录 | |||
| * @param {*} tran 事务,可以为null | |||
| * @returns | |||
| */ | |||
| delModel(meta, model, tran = null) { | |||
| return _deleteModel(this, meta, model, tran); | |||
| } | |||
| /** | |||
| * 获取一条记录 | |||
| * @param {string} tableName 表名 | |||
| * @param {object} model 要添加的记录 | |||
| * @param {*} tran 事务,可以为null | |||
| * @returns | |||
| */ | |||
| getModel(meta, model, tran = null) { | |||
| return _getModel(this, meta, model, tran); | |||
| } | |||
| /** | |||
| * 获取全部记录,可以查询 | |||
| * @param {string} tableName 表名 | |||
| * @param {object} model 要添加的记录 | |||
| * @param {*} tran 事务,可以为null | |||
| * @returns | |||
| */ | |||
| listAll(meta, query, pager = {}, tran = null) { | |||
| return _listAll(this, meta, query, pager, tran); | |||
| } | |||
| /** | |||
| * 分页获取记录,可以查询 | |||
| * @param {string} tableName 表名 | |||
| * @param {object} model 要添加的记录 | |||
| * @param {*} tran 事务,可以为null | |||
| * @returns | |||
| */ | |||
| async listPager(meta, query, pager, _count = 0, tran = null) { | |||
| const count = _count === 0 ? await _listCount(this, meta, query, tran) : _count; | |||
| const list = await _listPager(this, meta, query, pager, tran); | |||
| return { | |||
| count, | |||
| list | |||
| }; | |||
| } | |||
| } | |||
| MySQLHelp.prototype.add = _addModel; | |||
| @@ -0,0 +1,150 @@ | |||
| import webSQL from "./help.js"; | |||
| // 维护 | |||
| import createTable from "./table-create.js"; | |||
| import deleteTable from "./table-delete.js"; | |||
| export default { | |||
| _webSQLFlag: Symbol("nf-webSQL-help"), | |||
| _help: {}, // 访问数据库的实例 | |||
| _tables: {}, // 把表变成对象 | |||
| /** | |||
| * 根据参数创建一个数据库的实例,初始化数据库 | |||
| * * 删表、建表、添加默认数据 | |||
| * @param {*} info 参数 | |||
| * @returns 安装一个Vue的插件 | |||
| * * dbFlag: '数据库标识', | |||
| * * dbConfig: { // 连接数据库 | |||
| * * * dbName: 'vite2-blog', | |||
| * * * ver: 1.0, | |||
| * * * remarks: '测试用的博客数据库', | |||
| * * * size: 2 | |||
| * * }, | |||
| * * init: () => {}, | |||
| * * tables: { | |||
| * * * tableName: ['字段名称', '字段名称'] | |||
| * * }, | |||
| * * isDeleteOldTable: false, // 是否删除之前的表 | |||
| */ | |||
| createHelp(info) { | |||
| let webSQLFlag = this._webSQLFlag; | |||
| if (typeof info.dbFlag === "string") { | |||
| webSQLFlag = Symbol.for(info.dbFlag); | |||
| } else if (typeof info.dbFlag === "symbol") { | |||
| webSQLFlag = info.dbFlag; | |||
| } | |||
| const init = info.init; | |||
| // const tables = this._tables | |||
| // 连接数据库 | |||
| // eslint-disable-next-line new-cap | |||
| const help = new webSQL(info.dbConfig); | |||
| // 遍历配置,设置表的操作。 | |||
| // 按照表(对象)的配置信息,设置操作实例 | |||
| help.begin().then((cn) => { | |||
| for (const key in info.tables) { | |||
| const tableName = key; | |||
| const cols = Object.keys(info.tables[key]); | |||
| // 判断要不要删除表 | |||
| if (info.isDeleteOldTable) { | |||
| // 删除表 | |||
| deleteTable(help, tableName, cn); | |||
| } | |||
| // 建立表 | |||
| createTable(help, tableName, cols, cn); | |||
| } | |||
| }); | |||
| this._help[webSQLFlag] = help; | |||
| const newTable = {}; | |||
| help._tables = newTable; | |||
| this._tables[webSQLFlag] = newTable; | |||
| // 把表变成对象 | |||
| for (const key in info.tables) { | |||
| const meta = { | |||
| tableName: key, | |||
| cols: info.tables[key] | |||
| }; | |||
| newTable[meta.tableName] = { | |||
| add: (model, cn = null) => help.addModel(meta, model, cn), | |||
| get: (id = null, cn = null) => help.getModel(meta, id, cn), | |||
| put: (model, cn = null) => { | |||
| let _id = model; | |||
| if (typeof model === "object") { | |||
| _id = model[info.stores[key].id]; | |||
| } | |||
| return help.updateModel(meta, model, _id, cn); | |||
| }, | |||
| del: (model, cn = null) => { | |||
| let _id = model; | |||
| if (typeof model === "object") { | |||
| _id = model[info.stores[key].id]; | |||
| } | |||
| return help.delModel(meta, _id, cn); | |||
| }, | |||
| list: (query = {}, isDesc = false, cn = null) => { | |||
| const pager = { | |||
| orderBy: {} | |||
| }; | |||
| pager.orderBy[Object.keys(meta.cols)[0]] = isDesc; | |||
| return help.listAll(meta, query, pager, cn); | |||
| }, | |||
| pager: (query = {}, _pager = null, _count = 0, cn = null) => { | |||
| const pager = { | |||
| pagerIndex: 1, | |||
| pagerSize: 5, | |||
| pagerTotal: 100, | |||
| orderBy: {} | |||
| }; | |||
| pager.orderBy[Object.keys(meta.cols)[0]] = false; | |||
| if (_pager !== null) { | |||
| Object.assign(pager, _pager); | |||
| } | |||
| return help.listPager(meta, query, pager, _count, cn); | |||
| }, | |||
| begin: () => help.begin() | |||
| }; | |||
| } | |||
| if (typeof init === "function") { | |||
| init(help, newTable); | |||
| } | |||
| return { | |||
| // 安装插件,不用 provide 注入了 | |||
| install(app, options) { | |||
| // 注入状态,用 symbol 作为标记,避免重名 | |||
| // app.provide(webSQLFlag, { help, tables }) | |||
| // 调用初始化,给全局状态赋值 | |||
| } | |||
| }; | |||
| }, | |||
| // 获取数据库的连接实例 | |||
| useSQLHelp(_dbFlag) { | |||
| let flag = this._webSQLFlag; | |||
| if (typeof _dbFlag === "string") { | |||
| flag = Symbol.for(_dbFlag); | |||
| } else if (typeof _dbFlag === "symbol") { | |||
| flag = _dbFlag; | |||
| } | |||
| return this._help[flag]; | |||
| }, | |||
| /** | |||
| * 把表变成对象,可以直接用表.add/put/del/get等操作 | |||
| * @param {*} _dbFlag | |||
| * @returns | |||
| */ | |||
| useTables(_dbFlag) { | |||
| let flag = this._webSQLFlag; | |||
| if (typeof _dbFlag === "string") { | |||
| flag = Symbol.for(_dbFlag); | |||
| } else if (typeof _dbFlag === "symbol") { | |||
| flag = _dbFlag; | |||
| } | |||
| return this._tables[flag]; | |||
| } | |||
| }; | |||
| @@ -0,0 +1,50 @@ | |||
| import _getWhereQuery from "./_where-query.js"; | |||
| import _getPager from "./_pager-info.js"; | |||
| /** | |||
| * 不分页获取数据,可以查询 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { Object } query 查询条件 | |||
| * @param { Object } pager 排序字段 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * cols:{colName: '类型'}, 需要显示的字段 | |||
| * * query 结构(查询条件): | |||
| * * * { colName: [401, 11] } 字段名称,查询方式,查询关键字 | |||
| * * pager 结构: | |||
| * * * orderBy: { id: false } // 排序字段:字段名,false表示倒序。 | |||
| */ | |||
| export default function listAll(help, meta, query, pager, cn = null) { | |||
| // console.log('开始获取全部记录 :') | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| // 查询条件和查询参数 | |||
| const { whereQuery, whereValue } = _getWhereQuery(query); | |||
| // 设置排序 | |||
| const { orderBy } = _getPager(pager); | |||
| // 设置显示的字段 | |||
| const showCol = Object.keys(meta.cols); | |||
| if (showCol.length === 0) { | |||
| showCol.push("*"); | |||
| } | |||
| // 拼接查询语句 | |||
| const sql = `SELECT ${showCol.join(",")} FROM ${meta.tableName} ${whereQuery} ${orderBy}`; | |||
| // console.log('select-all-sql:', sql, whereValue) | |||
| help.query(sql, whereValue, cn) | |||
| .then((res) => { | |||
| // 添加成功 | |||
| // console.log('分页获取记录:', res) | |||
| resolve(Array.from(res.rows)); | |||
| }) | |||
| .catch((err) => { | |||
| // 出错了 | |||
| console.log("获取全部记录失败了:", err); | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| import _getWhereQuery from "./_where-query.js"; | |||
| import _getPager from "./_pager-info.js"; | |||
| /** | |||
| * 分页获取数据,可以查询 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { Object } query 查询条件 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * cols:{colName: '类型'}, 需要显示的字段 | |||
| * * query 结构(查询条件): | |||
| * * * { colName: [401, '查询关键字'] } 字段名称,查询方式,查询关键字 | |||
| */ | |||
| export default function getCount(help, meta, query) { | |||
| return new Promise((resolve, reject) => { | |||
| // 查询条件和查询参数 | |||
| const { whereQuery, whereValue } = _getWhereQuery(query); | |||
| // 统计总数 | |||
| const sql = `SELECT count(1) as count FROM ${meta.tableName} ${whereQuery} `; | |||
| console.log("count-sql:", sql, whereValue); | |||
| help.query(sql, whereValue) | |||
| .then((re) => { | |||
| resolve(re.rows[0].count); | |||
| }) | |||
| .catch((err) => { | |||
| // 出错了 | |||
| console.log("统计总记录数失败了:", err); | |||
| reject(err); | |||
| }); | |||
| }); | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| import _getWhereQuery from "./_where-query.js"; | |||
| import _getPager from "./_pager-info.js"; | |||
| /** | |||
| * 分页获取数据,可以查询 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { Object } meta 表、字段 | |||
| * @param { Object } query 查询条件 | |||
| * @param { Object } pager 数据 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * cols:{colName: '类型'}, 需要显示的字段 | |||
| * * query 结构(查询条件): | |||
| * * * { colName: [401, 11] } 字段名称,查询方式,查询关键字 | |||
| * * pager 结构: | |||
| * * * pageSize: 20 // 一页显示多少条记录 | |||
| * * * orderBy: { id: false } // 排序字段:字段名,false表示倒序。 | |||
| * * * pageTotal: 100 // 符合查询条件的总记录数 | |||
| * * * pageIndex: 1 // 显示第几页的记录,从 1 开始 | |||
| */ | |||
| export default function listPager(help, meta, query, pager) { | |||
| // console.log('开始分页 :') | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| // 查询条件和查询参数 | |||
| const { whereQuery, whereValue } = _getWhereQuery(query); | |||
| // 设置排序和分页 | |||
| const { orderBy, limit } = _getPager(pager); | |||
| // 设置显示的字段 | |||
| const showCol = Object.keys(meta.cols); | |||
| if (showCol.length === 0) { | |||
| showCol.push("*"); | |||
| } | |||
| // 拼接查询语句 | |||
| const sql = `SELECT ${showCol.join(",")} FROM ${meta.tableName} ${whereQuery} ${orderBy} ${limit}`; | |||
| console.log("select-pager-sql:", sql, whereValue); | |||
| help.query(sql, whereValue) | |||
| .then((res) => { | |||
| // 添加成功 | |||
| // console.log('分页获取记录:', res) | |||
| resolve(Array.from(res.rows)); | |||
| }) | |||
| .catch((err) => { | |||
| // 出错了 | |||
| console.log("分页获取记录失败了:", err); | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,39 @@ | |||
| /** | |||
| * 实现删除数据的功能。逻辑删除,update set flag = 1 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { string } tableName 表名 | |||
| * @param { array } cols 字段名集合,数组 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| * * * cols: {name: ''}, | |||
| */ | |||
| export default function createTable(help, tableName, cols, cn = null) { | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| // 记录字段名称,不设置类型了。 | |||
| let _cols = []; | |||
| if (typeof cols.length === "number") { | |||
| _cols = cols; | |||
| } else { | |||
| _cols = Object.keys(cols); | |||
| } | |||
| _cols = _cols.filter((key) => key.toLowerCase() !== "id"); | |||
| const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (id INTEGER PRIMARY KEY ASC, ${_cols.join(",")} )`; | |||
| // console.log('createSQL:', sql) | |||
| // 调用事务,建立表 | |||
| help.query(sql, [], cn) | |||
| .then((res) => { | |||
| resolve(res); | |||
| }) | |||
| .catch((err) => { | |||
| console.log("createTable -sql:", sql, err); | |||
| const stack = new Error(); | |||
| console.log("createTable -sql:", stack); | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| /** | |||
| * 实现删除数据的功能。逻辑删除,update set flag = 1 | |||
| * @param { MySQLHelp } help 访问数据库的实例 | |||
| * @param { string } tableName 表名 | |||
| * @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象 | |||
| * @returns 添加记录的ID | |||
| * * meta 结构: | |||
| * * * tableName: '', 表名 | |||
| * * * idKey: 'id', 主键字段名称 | |||
| * * * delFlag: 'isDel', 逻辑删除,标记字段名称 | |||
| */ | |||
| export default function deleteTable(help, tableName, cn = null) { | |||
| const myPromise = new Promise((resolve, reject) => { | |||
| const sql = `DROP TABLE ${tableName} `; | |||
| // console.log('sql:', sql) | |||
| help.query(sql, [], cn) | |||
| .then((res) => { | |||
| // 成功了,返回给调用者 | |||
| resolve(res); | |||
| }) | |||
| .catch((err) => { | |||
| console.log("deleteTable - sql:", sql, err); | |||
| reject(err); | |||
| }); | |||
| }); | |||
| return myPromise; | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| // 引入各种函数,便于做成npm包 | |||
| // indexedDB 部分 | |||
| import dbHelp from "./nf-ws-indexeddb/help.js"; | |||
| import dbInstall from "./nf-ws-indexeddb/install.js"; | |||
| // webSQL 部分 | |||
| import sqlHelp from "./nf-ws-websql/help.js"; | |||
| import sqlInstall from "./nf-ws-websql/install.js"; | |||
| // indexedDB 部分 | |||
| const dbCreateHelp = (info) => dbInstall.createHelp(info); | |||
| const useDBHelp = (_dbFlag) => dbInstall.useDBHelp(_dbFlag); | |||
| const useStores = (_dbFlag) => dbInstall.useStores(_dbFlag); | |||
| // webSQL 部分 | |||
| const sqlCreateHelp = (info) => sqlInstall.createHelp(info); | |||
| const useSQLHelp = (_dbFlag) => sqlInstall.useSQLHelp(_dbFlag); | |||
| const useTables = (_dbFlag) => sqlInstall.useTables(_dbFlag); | |||
| export { | |||
| // webSQL部分 | |||
| sqlHelp, | |||
| sqlCreateHelp, | |||
| useSQLHelp, | |||
| useTables, | |||
| // indexedDB 部分 | |||
| dbHelp, // indexedDB 的help | |||
| dbCreateHelp, // 创建help,初始化设置 | |||
| useDBHelp, // 组件里获取 help | |||
| useStores // 组件里获取对象仓库,方便实现增删改查 | |||
| }; | |||
| @@ -0,0 +1,127 @@ | |||
| const manageStorage = (type) => { | |||
| // type --- local: localStorage; | |||
| // session:sessionStorage | |||
| if (typeof type === "undefined" || type === "") { | |||
| type = "local"; | |||
| } | |||
| // 设置值 | |||
| const setItem = (key, value) => { | |||
| let v = value; | |||
| // 记录value的类型,默认是对象/数组 | |||
| let valueType = typeof value; | |||
| // 依据类型做序列化 | |||
| switch (valueType) { | |||
| case "object": | |||
| // 判断是不是日期类型 | |||
| if (value === null) { | |||
| valueType = "null"; | |||
| v = "null"; | |||
| } else if (value instanceof Date) { | |||
| // 保存数据的时间戳 | |||
| valueType = "date"; | |||
| v = value.valueOf(); | |||
| } else { | |||
| // 对象、数组 | |||
| v = JSON.stringify(value); | |||
| } | |||
| break; | |||
| case "function": | |||
| v = value.toString(); | |||
| break; | |||
| case "undefined": | |||
| valueType = "undefined"; | |||
| v = "undefined"; | |||
| break; | |||
| } | |||
| // 把数据、数据类型和时间戳,一起保存 | |||
| const object = { | |||
| valueType: valueType, | |||
| time: new Date().valueOf(), // 时间戳,判断是否过期 | |||
| value: v | |||
| }; | |||
| v = JSON.stringify(object); | |||
| if (type === "local") { | |||
| localStorage.setItem(key, v); | |||
| } else { | |||
| sessionStorage.setItem(key, v); | |||
| } | |||
| }; | |||
| // 获取值 | |||
| const getItem = (key) => { | |||
| let str = ""; | |||
| // 判断存储方式 | |||
| if (type === "local") { | |||
| str = localStorage.getItem(key); | |||
| } else { | |||
| str = sessionStorage.getItem(key); | |||
| } | |||
| // 判断是否为空 | |||
| if (typeof str === "undefined" || str === null || str === "") { | |||
| return str; | |||
| } | |||
| // 判断格式是否符合,没有太好的办法,暂时先这样。 | |||
| if (str.indexOf('{"valueType":"') === -1) { | |||
| return ""; | |||
| } | |||
| console.log("-----------------------------------------"); | |||
| console.log("111存储的数据的类型:", typeof str); | |||
| console.log("111存储的数据:", str); | |||
| // 把存储的数据转换为对象 | |||
| const object = JSON.parse(str); | |||
| // 取值 | |||
| let value = object.value; | |||
| // 判断存储之前的类型,做转换 | |||
| switch (object.valueType) { | |||
| case "object": // 对象和数组 | |||
| value = JSON.parse(value); | |||
| break; | |||
| case "function": // 不做转换 | |||
| // value = object.value | |||
| break; | |||
| case "date": // 日期的时间戳 | |||
| value = new Date(value); | |||
| break; | |||
| case "number": // 数字 | |||
| value = parseInt(value); | |||
| break; | |||
| case "null": | |||
| value = null; | |||
| break; | |||
| case "undefined": | |||
| value = undefined; | |||
| break; | |||
| } | |||
| console.log("存储的数据的类型:"); | |||
| console.log(object.valueType, typeof value); | |||
| console.log(object.valueType, Object.prototype.toString.call(value)); | |||
| console.log("存储的数据:", value); | |||
| return value; | |||
| }; | |||
| // removeItem | |||
| const removeItem = (key) => { | |||
| if (type === "local") { | |||
| localStorage.removeItem(key); | |||
| } else { | |||
| sessionStorage.removeItem(key); | |||
| } | |||
| }; | |||
| // clear | |||
| const clear = (key) => { | |||
| if (type === "local") { | |||
| localStorage.clear(); | |||
| } else { | |||
| sessionStorage.clear(); | |||
| } | |||
| }; | |||
| return { | |||
| setItem, | |||
| getItem, | |||
| removeItem, | |||
| clear | |||
| }; | |||
| }; | |||
| @@ -0,0 +1,415 @@ | |||
| // indexedDB.js,浏览器本地数据库操作 | |||
| export default { | |||
| // indexedDB兼容 | |||
| indexedDB: window.indexedDB || window.webkitindexedDB || window.msIndexedDB || mozIndexedDB, | |||
| // 打开数据库 | |||
| // 新对象储存空间newStore参数:newStore.name、newStore.key | |||
| // 新增对象存储空间要更改数据库版本 | |||
| openDB: function(dbname, version, db, newStore, callback) { | |||
| var version = version; | |||
| var request = this.indexedDB.open(dbname, version); | |||
| request.onerror = function(event) { | |||
| console.log("IndexedDB数据库打开错误"); | |||
| }; | |||
| request.onsuccess = function(event) { | |||
| db = event.target.result; | |||
| if (callback && typeof callback === "function") { | |||
| callback(db); | |||
| } | |||
| }; | |||
| // onupgradeneeded,调用创建新的储存空间 | |||
| request.onupgradeneeded = function(event) { | |||
| var db = event.target.result; | |||
| if (newStore) { | |||
| if (!db.objectStoreNames.contains(newStore.name)) { | |||
| var objectStore = db.createObjectStore(newStore.name, { | |||
| keyPath: newStore.key | |||
| }); | |||
| objectStore.createIndex("counter_index", "counter", { unique: false }); | |||
| objectStore.createIndex("barcode_index", "barcode", { unique: false }); | |||
| objectStore.createIndex("qty_index", "qty", { unique: false }); | |||
| objectStore.createIndex("counter_code", ["counter", "barcode"], { unique: false }); | |||
| } | |||
| } | |||
| }; | |||
| }, | |||
| // 删除数据库 | |||
| deleteDB: function(dbname, callback) { | |||
| var deleteQuest = this.indexedDB.deleteDatabase(dbname); | |||
| deleteQuest.onerror = function() { | |||
| console.log("删除数据库出错"); | |||
| }; | |||
| deleteQuest.onsuccess = function() { | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }; | |||
| }, | |||
| // 关闭数据库 | |||
| closeDB: function(dbname) { | |||
| dbname.close(); | |||
| console.log("数据库已关闭"); | |||
| }, | |||
| // 更新旧值,针对输入数量 | |||
| putData: function(db, storename, dataArr, callback) { | |||
| let mybarcode = ""; | |||
| let QTY = ""; | |||
| let key = ""; | |||
| let counter = ""; | |||
| let barcode = ""; | |||
| let addtime = ""; | |||
| dataArr.forEach((item) => { | |||
| mybarcode = item.barcode; | |||
| QTY = item.qty; | |||
| barcode = item.barcode; | |||
| counter = item.counter; | |||
| key = item.counterCode; | |||
| addtime = item.addtime; | |||
| }); | |||
| this.getdatabycursor(db, storename).then((arr) => { | |||
| if (arr.length == 0) { | |||
| //console.log("添加") | |||
| var store = db.transaction(storename, "readwrite").objectStore(storename), | |||
| request; | |||
| for (var i = 0, len = dataArr.length; i < len; i++) { | |||
| request = store.put(dataArr[i]); | |||
| request.onerror = function() { | |||
| console.error("PUT添加数据报错"); | |||
| }; | |||
| request.onsuccess = function(result) { | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }; | |||
| } | |||
| } else { | |||
| this.read(db, storename, counter, barcode).then((x) => { | |||
| if (x) { | |||
| //console.log("最新的值是" + QTY) | |||
| this.updateDataByKey(db, storename, key, QTY, addtime).then((x) => { | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }); | |||
| } else { | |||
| //console.log("再次添加") | |||
| var store = db.transaction(storename, "readwrite").objectStore(storename), | |||
| request; | |||
| for (var i = 0, len = dataArr.length; i < len; i++) { | |||
| request = store.put(dataArr[i]); | |||
| request.onerror = function() { | |||
| console.error("PUT添加数据报错"); | |||
| }; | |||
| request.onsuccess = function(result) { | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }, | |||
| // 更新旧值 | |||
| putDatas: function(db, storename, dataArr, callback) { | |||
| let mybarcode = ""; | |||
| let QTY = ""; | |||
| let key = ""; | |||
| let counter = ""; | |||
| let barcode = ""; | |||
| let addtime = ""; | |||
| dataArr.forEach((item) => { | |||
| mybarcode = item.barcode; | |||
| QTY = item.qty; | |||
| key = item.counterCode; | |||
| counter = item.counter; | |||
| barcode = item.barcode; | |||
| addtime = item.addtime; | |||
| }); | |||
| this.getdatabycursor(db, storename).then((arr) => { | |||
| if (arr.length == 0) { | |||
| //console.log("添加") | |||
| var store = db.transaction(storename, "readwrite").objectStore(storename), | |||
| request; | |||
| for (var i = 0, len = dataArr.length; i < len; i++) { | |||
| request = store.add(dataArr[i]); | |||
| request.onerror = function() { | |||
| console.error("PUT添加数据报错"); | |||
| }; | |||
| request.onsuccess = function(result) { | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }; | |||
| } | |||
| } else { | |||
| this.read(db, storename, counter, barcode).then((x) => { | |||
| if (x) { | |||
| this.updateDataByKeys(db, storename, key, addtime).then((x) => { | |||
| this.getdata(db, storename).then((result) => { | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }); | |||
| }); | |||
| } else { | |||
| //console.log("再次添加") | |||
| //console.log("当前的值是"+barcode) | |||
| var store = db.transaction(storename, "readwrite").objectStore(storename), | |||
| request; | |||
| for (var i = 0, len = dataArr.length; i < len; i++) { | |||
| request = store.add(dataArr[i]); | |||
| request.onerror = function() { | |||
| console.error("PUT添加数据报错"); | |||
| }; | |||
| request.onsuccess = function(result) { | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }, | |||
| //根据key修改数量 | |||
| updateDataByKey: function(db, storeName, value, QTY, addtime) { | |||
| var transaction = db.transaction(storeName, "readwrite"); | |||
| var store = transaction.objectStore(storeName); | |||
| var request = store.get(value); | |||
| return new Promise((resolve, reject) => { | |||
| request.onsuccess = function(e) { | |||
| var stocktable = e.target.result; | |||
| if (stocktable) { | |||
| stocktable.qty = QTY; | |||
| stocktable.addtime = addtime; | |||
| resolve(store.put(stocktable)); | |||
| } else { | |||
| reject(false); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| updateDataBycode: function(db, storeName, value, QTY) { | |||
| var transaction = db.transaction(storeName, "readwrite"); | |||
| var store = transaction.objectStore(storeName); | |||
| var request = store.get(value); | |||
| return new Promise((resolve, reject) => { | |||
| request.onsuccess = function(e) { | |||
| var stocktable = e.target.result; | |||
| if (stocktable) { | |||
| stocktable.qty = QTY; | |||
| resolve(store.put(stocktable)); | |||
| } else { | |||
| reject(false); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| //根据key修改数量 | |||
| updateDataByKeys: function(db, storeName, value, addtime, callback) { | |||
| var transaction = db.transaction(storeName, "readwrite"); | |||
| var store = transaction.objectStore(storeName); | |||
| var request = store.get(value); | |||
| return new Promise((resolve, reject) => { | |||
| //console.log(addtime) | |||
| request.onsuccess = function(e) { | |||
| var stocktable = e.target.result; | |||
| if (stocktable) { | |||
| stocktable.qty = QTY; | |||
| stocktable.addtime = addtime; | |||
| resolve(store.put(stocktable)); | |||
| } else { | |||
| reject(false); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| // 删除数据 | |||
| deleteData: function(db, storename, key, callback) { | |||
| var store = db.transaction(storename, "readwrite").objectStore(storename); | |||
| store.delete(key); | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }, | |||
| // 清空数据 | |||
| clearData: function(db, storename, callback) { | |||
| var store = db.transaction(storename, "readwrite").objectStore(storename); | |||
| store.clear(); | |||
| if (callback && typeof callback === "function") { | |||
| callback(); | |||
| } | |||
| }, | |||
| // 通过key获取数据 | |||
| read: function(db, storeName, counter, barcode) { | |||
| var transaction = db.transaction(storeName); | |||
| var objectStore = transaction.objectStore(storeName); | |||
| var currentdata = [counter, barcode]; | |||
| var indexs = objectStore.index("counter_code"); | |||
| var request = indexs.openCursor(IDBKeyRange.only(currentdata)); | |||
| return new Promise((resolve, reject) => { | |||
| request.onsuccess = function(e) { | |||
| var cursor = e.target.result; | |||
| if (cursor) { | |||
| resolve(true); | |||
| } else { | |||
| resolve(false); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| // 通过barcode获取数据 | |||
| reads: function(db, storeName, values) { | |||
| var transaction = db.transaction(storeName); | |||
| var objectStore = transaction.objectStore(storeName); | |||
| var indexs = objectStore.index("barcode_index"); | |||
| var data = []; | |||
| var request = indexs.openCursor(IDBKeyRange.only(values)); | |||
| return new Promise((resolve, reject) => { | |||
| request.onsuccess = function(e) { | |||
| var cursor = e.target.result; | |||
| if (cursor) { | |||
| data.push(cursor.value); | |||
| // resolve(data); | |||
| cursor.continue(); | |||
| } else { | |||
| resolve(data); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| //根据counter索引查询数据 | |||
| getdatabyCounter: function(db, storeName, values) { | |||
| var transaction = db.transaction(storeName); | |||
| var store = transaction.objectStore(storeName); | |||
| var indexs = store.index("counter_index"); | |||
| var datas = []; | |||
| var request = indexs.openCursor(IDBKeyRange.only(values)); | |||
| return new Promise((resolve, reject) => { | |||
| request.onsuccess = function(e) { | |||
| var cursor = e.target.result; | |||
| if (cursor) { | |||
| datas.push(cursor.value); | |||
| cursor.continue(); | |||
| } else { | |||
| resolve(datas); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| //根据主键和索引查询 | |||
| getAll: function(db, storeName, counter, barcode) { | |||
| var transaction = db.transaction(storeName); | |||
| var objectStore = transaction.objectStore(storeName); | |||
| var counterCode = [counter, barcode]; | |||
| var indexs = objectStore.index("counter_code"); | |||
| var request = indexs.openCursor(IDBKeyRange.only(counterCode)); | |||
| var data = []; | |||
| return new Promise((resolve, reject) => { | |||
| request.onsuccess = function(e) { | |||
| var cursor = e.target.result; | |||
| if (cursor) { | |||
| data.push(cursor.value); | |||
| //resolve(data); | |||
| cursor.continue(); | |||
| } else { | |||
| resolve(data); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| //根据key查询数量是否存在 | |||
| getqtyBykey: function(db, storeName, key) { | |||
| var transaction = db.transaction(storeName); | |||
| var objectStore = transaction.objectStore(storeName); | |||
| var request = objectStore.get(key); | |||
| request.onerror = function(event) { | |||
| console.log("事务失败"); | |||
| }; | |||
| return new Promise((resolve, reject) => { | |||
| request.onsuccess = function(event) { | |||
| if (request.result) { | |||
| //console.log(request.result.qty) | |||
| resolve(request.result); | |||
| } else { | |||
| resolve(false); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| // //通过游标遍历数据 | |||
| getdatabycursor: function(db, storename) { | |||
| var objectStore = db.transaction(storename).objectStore(storename); | |||
| var dataList = []; | |||
| var i = 0; | |||
| return new Promise((resolve, reject) => { | |||
| objectStore.openCursor().onsuccess = function(event) { | |||
| var cursor = event.target.result; | |||
| if (cursor) { | |||
| dataList.push(cursor.value); | |||
| cursor.continue(); | |||
| } else { | |||
| resolve(dataList); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| //查询所有的柜台 | |||
| getAllCounter: function(db, storename) { | |||
| var transaction = db.transaction(storename); | |||
| var store = transaction.objectStore(storename); | |||
| var indexs = store.index("counter_index"); | |||
| var data = []; | |||
| return new Promise((resolve, reject) => { | |||
| indexs.openCursor().onsuccess = function(e) { | |||
| var cursor = e.target.result; | |||
| if (cursor) { | |||
| // console.log(cursor.value.counter); | |||
| data.push(cursor.value.counter); | |||
| resolve(data); | |||
| cursor.continue(); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| getdata: function(db, storename) { | |||
| var objectStore = db.transaction(storename).objectStore(storename); | |||
| var data = []; | |||
| return new Promise((resolve, reject) => { | |||
| objectStore.openCursor().onsuccess = function(event) { | |||
| var cursor = event.target.result; | |||
| if (cursor) { | |||
| data.push(cursor.value); | |||
| resolve(data); | |||
| } else { | |||
| reject(false); | |||
| } | |||
| }; | |||
| }); | |||
| }, | |||
| getqtybyqtyindex: function(db, storename) { | |||
| var transaction = db.transaction(storename); | |||
| var store = transaction.objectStore(storename); | |||
| var indexs = store.index("qty_index"); | |||
| var sum = 0; | |||
| return new Promise((resolve, reject) => { | |||
| indexs.openCursor().onsuccess = function(e) { | |||
| var cursor = e.target.result; | |||
| if (cursor) { | |||
| sum += cursor.value.qty; | |||
| cursor.continue(); | |||
| } else { | |||
| resolve(sum); | |||
| } | |||
| }; | |||
| }); | |||
| } | |||
| }; | |||
| @@ -0,0 +1,10 @@ | |||
| import Vue from "vue"; | |||
| import VueLazyload from "vue-lazyload"; | |||
| // https://github.com/hilongjw/vue-lazyload | |||
| Vue.use(VueLazyload, { | |||
| preLoad: 1.3, | |||
| // loading: require("@/assets/ic_photo_loading.png"), | |||
| error: require("@/assets/ic_photo_error.png"), | |||
| attempt: 3 | |||
| }); | |||
| @@ -0,0 +1,16 @@ | |||
| import Vue from "vue"; | |||
| import { Indicator } from "mint-ui"; | |||
| // Mint UI 使用文档: https://mint-ui.github.io/docs/#/zh-cn2 | |||
| Vue.component(Indicator); | |||
| // import isEmpty from "lodash/isEmpty"; | |||
| Vue.prototype.showLoading = function(msg) { | |||
| // if (isEmpty(msg)) { | |||
| // msg = "加载中..."; | |||
| // } | |||
| return Indicator.open(msg); | |||
| }; | |||
| Vue.prototype.hideLoading = function() { | |||
| return Indicator.close(); | |||
| }; | |||
| @@ -0,0 +1,149 @@ | |||
| var websock = null; | |||
| // let rec; //断线重连后,延迟5秒重新创建WebSocket连接 rec用来存储延迟请求的代码 | |||
| let isConnect = false; //连接标识 避免重复连接 | |||
| let checkMsg = "heartbeat"; //心跳发送/返回的信息 服务器和客户端收到的信息内容如果如下 就识别为心跳信息 不要做业务处理 | |||
| var globalCallback = function() {}; | |||
| let createWebSocket = (url) => { | |||
| try { | |||
| initWebSocket(url); //初始化websocket连接 | |||
| } catch (e) { | |||
| console.log("尝试创建连接失败"); | |||
| reConnect(url); //如果无法连接上webSocket 那么重新连接!可能会因为服务器重新部署,或者短暂断网等导致无法创建连接 | |||
| } | |||
| }; | |||
| //定义重连函数 | |||
| let reConnect = (url) => { | |||
| console.log("尝试重新连接"); | |||
| if (isConnect) return; //如果已经连上就不在重连了 | |||
| window.socketTimer && window.clearTimeout(window.socketTimer); | |||
| window.socketTimer = setTimeout(function() { | |||
| // 延迟5秒重连 避免过多次过频繁请求重连 | |||
| createWebSocket(url); | |||
| }, 5000); | |||
| }; | |||
| //设置关闭连接 | |||
| let closeWebSocket = () => { | |||
| webSocket.close(); | |||
| }; | |||
| //心跳设置 | |||
| var heartCheck = { | |||
| timeout: 20000, //每段时间发送一次心跳包 这里设置为20s | |||
| timeoutObj: null, //延时发送消息对象(启动心跳新建这个对象,收到消息后重置对象) | |||
| start: function() { | |||
| this.timeoutObj = setTimeout(function() { | |||
| if (isConnect) websock.send(checkMsg); | |||
| }, this.timeout); | |||
| }, | |||
| reset: function() { | |||
| window.clearTimeout(this.timeoutObj); | |||
| this.start(); | |||
| } | |||
| }; | |||
| // 初始化websocket | |||
| function initWebSocket(ws) { | |||
| // ws地址 -->这里是你的请求路径 | |||
| websock = new WebSocket(ws); | |||
| websock.onmessage = function(e) { | |||
| websocketonmessage(e); | |||
| }; | |||
| websock.onclose = function(e) { | |||
| websocketclose(e); | |||
| }; | |||
| websock.onopen = function() { | |||
| websocketOpen(); | |||
| // heartCheck.start(); | |||
| }; | |||
| // 连接发生错误的回调方法 | |||
| websock.onerror = function() { | |||
| console.log("WebSocket连接发生错误"); | |||
| isConnect = false; //连接断开修改标识 | |||
| reConnect(ws); //连接错误 需要重连 | |||
| }; | |||
| return websock; | |||
| } | |||
| // 实际调用的方法 | |||
| function sendSock(agentData, callback) { | |||
| globalCallback = callback; | |||
| // console.log(globalCallback) | |||
| if (websock.readyState === websock.OPEN) { | |||
| // 若是ws开启状态 | |||
| websocketsend(agentData); | |||
| } else if (websock.readyState === websock.CONNECTING) { | |||
| // 若是 正在开启状态,则等待1s后重新调用 | |||
| setTimeout(function() { | |||
| sendSock(agentData, callback); | |||
| }, 1000); | |||
| } else { | |||
| // 若未开启 ,则等待1s后重新调用 | |||
| setTimeout(function() { | |||
| sendSock(agentData, callback); | |||
| }, 1000); | |||
| } | |||
| } | |||
| function getSock(callback) { | |||
| globalCallback = callback; | |||
| } | |||
| // 数据接收 | |||
| function websocketonmessage(e) { | |||
| console.log(e.data); | |||
| let O_o = JSON.parse(decodeUnicode(e.data)); | |||
| if (!O_o) { | |||
| heartCheck.reset(); | |||
| } else { | |||
| if (O_o.msg == "open success") { | |||
| sessionStorage.setItem("wid", O_o.wid); | |||
| } else { | |||
| console.log(O_o); | |||
| globalCallback(O_o); | |||
| } | |||
| } | |||
| // globalCallback(JSON.parse(e.data)) | |||
| function decodeUnicode(str) { | |||
| str = str.replace(/\\/g, "%"); | |||
| //转换中文 | |||
| str = unescape(str); | |||
| //将其他受影响的转换回原来 | |||
| str = str.replace(/%/g, "\\"); | |||
| //对网址的链接进行处理 | |||
| str = str.replace(/\\/g, ""); | |||
| return str; | |||
| } | |||
| } | |||
| // 数据发送 | |||
| function websocketsend(agentData) { | |||
| console.log(JSON.stringify(agentData)); | |||
| websock.send(JSON.stringify(agentData)); | |||
| } | |||
| // 关闭 | |||
| function websocketclose(e) { | |||
| console.log(e); | |||
| isConnect = false; //断开后修改标识 | |||
| console.log("connection closed (" + e.code + ")"); | |||
| let url = e.currentTarget.url; | |||
| reConnect(url); | |||
| } | |||
| // 创建 websocket 连接 | |||
| function websocketOpen(e) { | |||
| isConnect = true; | |||
| console.log("连接成功"); | |||
| } | |||
| // initWebSocket(); | |||
| // 将方法暴露出去 | |||
| export { initWebSocket, sendSock, getSock, createWebSocket, closeWebSocket }; | |||
| @@ -0,0 +1,135 @@ | |||
| import axios from "axios"; | |||
| import Vue from "vue"; | |||
| let retry = 2; | |||
| let retryDelay = 1000; | |||
| class AxiosRequest { | |||
| constructor(baseUrl, isDebuggable) { | |||
| this.isDebuggable = isDebuggable; | |||
| this.instance = axios.create({ | |||
| baseURL: baseUrl, | |||
| timeout: 10000, // 10s as default | |||
| withCredentials: true, | |||
| headers: { | |||
| "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" | |||
| } | |||
| }); | |||
| this.interceptors(); | |||
| } | |||
| get(url, params) { | |||
| return this.request({ | |||
| method: "get", | |||
| url: url, | |||
| params: params | |||
| }); | |||
| } | |||
| post(url, params) { | |||
| return this.request({ | |||
| method: "post", | |||
| url: url, | |||
| headers: { | |||
| "Content-Type": "application/json;charset=UTF-8" | |||
| }, | |||
| data: params | |||
| }); | |||
| } | |||
| postForm(url, params) { | |||
| return this.request({ | |||
| method: "post", | |||
| url: url, | |||
| data: params, | |||
| transformRequest: [ | |||
| function(data) { | |||
| let ret = ""; | |||
| for (const it in data) { | |||
| ret += encodeURIComponent(it) + "=" + encodeURIComponent(data[it]) + "&"; | |||
| } | |||
| return ret; | |||
| } | |||
| ] | |||
| }); | |||
| } | |||
| postFile(url, params) { | |||
| return this.request({ | |||
| method: "post", | |||
| url: url, | |||
| headers: { | |||
| "Content-Type": "multipart/form-data" | |||
| }, | |||
| data: params, | |||
| transformRequest: [ | |||
| function(data) { | |||
| const formData = new FormData(); | |||
| for (const item in data) { | |||
| formData.append(item, data[item]); | |||
| } | |||
| return formData; | |||
| } | |||
| ] | |||
| }); | |||
| } | |||
| request(options) { | |||
| return this.instance(options); | |||
| } | |||
| interceptors() { | |||
| let that = this; | |||
| this.instance.interceptors.response.use( | |||
| (res) => { | |||
| const { data, status } = res; | |||
| if (status > 200 && status < 400) { | |||
| if (this.isDebuggable) { | |||
| window.Cloudia.showToast(`http code: ${status}`); | |||
| } | |||
| } else if (status !== 200) { | |||
| if (this.isDebuggable) { | |||
| window.Cloudia.showToast(`http code: ${status}`); | |||
| } else { | |||
| console.error(`http error, code: ${status}`); | |||
| window.Cloudia.showToast("请检查网络连接是否正常,稍后重试!"); | |||
| } | |||
| } | |||
| if (data.resultInfo && data.resultInfo.resultCode !== "200") { | |||
| window.Cloudia.showToast( | |||
| `request error: ${data.resultInfo.resultMsg} (${data.resultInfo.resultCode})` | |||
| ); | |||
| } | |||
| return data; | |||
| }, | |||
| (error) => { | |||
| window.Cloudia.showToast("请检查网络连接是否正常,稍后重试!"); | |||
| console.error("网络加载失败"); | |||
| var config = error.config; | |||
| config.__retryCount = config.__retryCount || 0; | |||
| if (config.__retryCount >= retry) { | |||
| console.error("网络请求重试结束:" + config.__retryCount); | |||
| Vue.prototype.hideLoading(); | |||
| // Reject with the error | |||
| return Promise.reject(error); | |||
| } | |||
| // Increase the retry count | |||
| config.__retryCount += 1; | |||
| console.error("网络请求开始重试:" + config.__retryCount); | |||
| // Create new promise to handle exponential backoff | |||
| return new Promise(function(resolve) { | |||
| setTimeout(function() { | |||
| resolve(); | |||
| }, retryDelay); | |||
| }).then(function() { | |||
| return that.request(config); | |||
| }); | |||
| } | |||
| ); | |||
| } | |||
| } | |||
| export default AxiosRequest; | |||
| @@ -0,0 +1,41 @@ | |||
| import request from "./request"; | |||
| // https://{host-name}/api/apilist?api=Weather | |||
| // location - 城市 | |||
| // startTime - 2019-05-09/2019-05-09 12:00 | |||
| // endTime - 2019-05-12/2019-05-12 12:00 | |||
| // lang - Optional, en/cn/jp | |||
| export const getWeatherDaily = (location, startTime, endTime, lang = "cn") => { | |||
| console.info( | |||
| "request corsapi/weather?api=Weather, params:location=" + | |||
| location + | |||
| ", startTime=" + | |||
| startTime + | |||
| ", endTime=" + | |||
| endTime + | |||
| ", lang=" + | |||
| lang | |||
| ); | |||
| return request.get("corsapi/weather?api=WeatherNow", { | |||
| location: location, | |||
| lang: lang | |||
| }); | |||
| }; | |||
| export const getWeatherWeekly = (location, startTime, endTime, lang = "cn") => { | |||
| console.info( | |||
| "request corsapi/weather?api=Weather, params:location=" + | |||
| location + | |||
| ", startTime=" + | |||
| startTime + | |||
| ", endTime=" + | |||
| endTime + | |||
| ", lang=" + | |||
| lang | |||
| ); | |||
| return request.get("corsapi/weather?api=Weather", { | |||
| location: location, | |||
| startTime: startTime, | |||
| endTime: endTime, | |||
| lang: lang | |||
| }); | |||
| }; | |||
| @@ -0,0 +1,5 @@ | |||
| import AxiosRequest from "./axios"; | |||
| import config from "@/config"; | |||
| const request = new AxiosRequest(config.baseUrl, config.isDebuggable); | |||
| export default request; | |||
| @@ -0,0 +1,87 @@ | |||
| const { add, query } = require("../db/dbHelp"); | |||
| const express = require("express"); | |||
| const router = express.Router(); | |||
| // 连接数据库 | |||
| const jsonWrite = function(res, ret) { | |||
| if (typeof ret === "undefined") { | |||
| res.json({ | |||
| code: "1", | |||
| msg: "操作失败" | |||
| }); | |||
| } else { | |||
| res.json(ret); | |||
| } | |||
| }; | |||
| // 接口:增加信息sql,编辑修改信息sql1 | |||
| router.post("/save", (req, res) => { | |||
| const params = req.body; | |||
| console.log(params); | |||
| add(params, (result) => { | |||
| jsonWrite(res, result); | |||
| }); | |||
| }); | |||
| // 接口:用户管理分页接口查询 | |||
| router.get("/getlist", (req, res) => { | |||
| const params = req.body; | |||
| console.log(params); | |||
| query((result) => { | |||
| jsonWrite(res, result); | |||
| }); | |||
| }); | |||
| const db = require("../db/mssql"); //注意改路径 | |||
| router.get("/ms/detailMD", (req, res) => { | |||
| console.log(db.sql); | |||
| //根据时间查询的模板 | |||
| var count = 0; | |||
| var commonResult = []; | |||
| db.sql( | |||
| "SELECT PassType,count(1) as Count FROM HJ_PersonRecognition WHERE DateDiff(dd,DetectTime,getdate())=0 group by PassType order by PassType", | |||
| function(err, result) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| commonResult.push(...result.recordset); | |||
| count++; | |||
| if (count >= 3) { | |||
| res.send(commonResult); | |||
| } | |||
| } | |||
| ); | |||
| db.sql( | |||
| "select 0 as Reserve1, count(1) as cnt from HJ_PersonRecognition as person where person.Reserve1 = '0' and DATEDIFF(DAY, person.DetectTime, GETDATE()) = 0 union select 1 as Reserve1, count(1) as cnt from HJ_PersonRecognition as person where person.Reserve1 = '1' and DATEDIFF(DAY, person.DetectTime, GETDATE()) = 0", | |||
| function(err, result) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| commonResult.push(...result.recordset); | |||
| count++; | |||
| if (count >= 3) { | |||
| res.send(commonResult); | |||
| } | |||
| } | |||
| ); | |||
| db.sql( | |||
| "SELECT 0 as online, count(1) as num FROM HJ_EquipInfo WHERE Status = 1 and EquipTypeID = 3 union SELECT 1 as online, count(1) as num FROM HJ_EquipInfo WHERE (Status = 0 or Status = 2) and EquipTypeID = 3", | |||
| function(err, result) { | |||
| if (err) { | |||
| console.log(err); | |||
| return; | |||
| } | |||
| commonResult.push(...result.recordset); | |||
| count++; | |||
| if (count >= 3) { | |||
| res.send(commonResult); | |||
| } | |||
| } | |||
| ); | |||
| }); | |||
| module.exports = router; | |||
| @@ -0,0 +1,72 @@ | |||
| import Vue from "vue"; | |||
| import VueRouter from "vue-router"; | |||
| import ApiDemo from "../views/ApiDemo"; | |||
| import meetingAppoint from "../views/MeetingAppoint"; | |||
| import meetingReserve from "../views/MeetingReserve"; | |||
| const meetingCancel = (r) => require.ensure([], () => r(require("../views/MeetingCancel")), "meetingCancel"); | |||
| const visitorAppoint = (r) => require.ensure([], () => r(require("../views/VisitorAppoint")), "visitorAppoint"); | |||
| const visitorRegister = (r) => require.ensure([], () => r(require("../views/VisitorRegister")), "visitorRegister"); | |||
| const visitor = (r) => require.ensure([], () => r(require("../views/Visitor")), "visitor"); | |||
| Vue.use(VueRouter); | |||
| const routes = [ | |||
| { | |||
| path: "/", | |||
| name: "apiDemo", | |||
| component: ApiDemo | |||
| }, | |||
| { | |||
| path: "/meetingReserve", | |||
| name: "meetingReserve", | |||
| component: meetingReserve | |||
| }, | |||
| { | |||
| path: "/meetingAppoint", | |||
| name: "meetingAppoint", | |||
| component: meetingAppoint | |||
| }, | |||
| { | |||
| path: "/meetingCancel", | |||
| name: "meetingCancel", | |||
| component: meetingCancel | |||
| }, | |||
| { | |||
| path: "/visitorAppoint", | |||
| name: "visitorAppoint", | |||
| component: visitorAppoint | |||
| }, | |||
| { | |||
| path: "/visitorRegister", | |||
| name: "visitorRegister", | |||
| component: visitorRegister | |||
| }, | |||
| { | |||
| path: "/visitor", | |||
| name: "visitor", | |||
| component: visitor | |||
| } | |||
| ]; | |||
| const router = new VueRouter({ | |||
| mode: "history", | |||
| base: process.env.BASE_URL, | |||
| routes | |||
| }); | |||
| /* | |||
| router.beforeEach((to, from, next) => { | |||
| if (to.path == "/") { | |||
| if (to.fullPath.indexOf("_r") > -1) { | |||
| next(); | |||
| } else { | |||
| if (to.fullPath.indexOf("?") > -1) { | |||
| next(to.fullPath + "&_r=" + Math.random()); | |||
| } else { | |||
| next(to.fullPath + "?_r=" + Math.random()); | |||
| } | |||
| } | |||
| } | |||
| });*/ | |||
| export default router; | |||
| @@ -0,0 +1,42 @@ | |||
| const ws = require("nodejs-websocket"); | |||
| console.log("开始建立连接..."); | |||
| const socket = ws | |||
| .createServer(function(conn) { | |||
| let msg = { id: "211", times: 0, name: "王武", tag: "" }; | |||
| let intval = setInterval(() => { | |||
| if (conn.readyState == 1) { | |||
| conn.sendText(JSON.stringify(msg)); | |||
| } else { | |||
| clearInterval(intval); | |||
| } | |||
| }, 2000); | |||
| conn.on("text", function(str) { | |||
| /* console.log("message:" + str); | |||
| let msg = { data: { on: 1, face: "hhhh" } }; | |||
| // let msg = "你好,这里是是日前端~"; | |||
| // let msg = 'websocket处于正常状态' | |||
| setInterval(() => { | |||
| conn.sendText(JSON.stringify(msg)); | |||
| }, 3000);*/ | |||
| }); | |||
| conn.on("connect", function(code) { | |||
| let msg = { data: { on: 1, face: "hhhh" } }; | |||
| // let msg = "你好,这里是是日前端~"; | |||
| // let msg = 'websocket处于正常状态' | |||
| setInterval(() => { | |||
| conn.sendText(JSON.stringify(msg)); | |||
| }, 3000); | |||
| }); | |||
| conn.on("close", function(code, reason) { | |||
| console.log("关闭连接"); | |||
| }); | |||
| conn.on("error", function(code, reason) { | |||
| console.log("异常关闭", code, reason); | |||
| }); | |||
| }) | |||
| .listen(8666); | |||
| console.log("WebSocket建立完毕"); | |||
| module.exports = socket; | |||
| @@ -0,0 +1,28 @@ | |||
| // node 后端服务器 | |||
| const dbRouter = require("../router/dbRouter"); | |||
| const bodyParser = require("body-parser"); | |||
| const express = require("express"); | |||
| const app = express(); | |||
| //采用设置所有均可访问的方法解决跨域问题 | |||
| app.all("*", function(req, res, next) { | |||
| //设置允许跨域的域名,*代表允许任意域名跨域 | |||
| res.header("Access-Control-Allow-Origin", "*"); | |||
| //允许的header类型 | |||
| res.header("Access-Control-Allow-Headers", "content-type"); | |||
| //跨域允许的请求方式 | |||
| res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); | |||
| if (req.method.toLowerCase() == "options") res.send(200); | |||
| //让options尝试请求快速结束 | |||
| else next(); | |||
| }); | |||
| // 以json格式返回出去 | |||
| app.use(bodyParser.json()); | |||
| app.use(bodyParser.urlencoded({ extended: false })); | |||
| // 后端api路由 | |||
| app.use("/api/db", dbRouter); | |||
| // 监听端口 | |||
| app.listen(13000); | |||
| console.log("success listen at port:13000......"); | |||
| @@ -0,0 +1,56 @@ | |||
| import Vue from "vue"; | |||
| import Vuex from "vuex"; | |||
| Vue.use(Vuex); | |||
| const store = new Vuex.Store({ | |||
| state() { | |||
| return { | |||
| // 就是公共的数据,所有的组件都可以直接使用 | |||
| count: 100, | |||
| lastReq: "", | |||
| lastPerson: {}, | |||
| progressPerson: {}, | |||
| localDevInfo: {}, | |||
| handleType: "", | |||
| pad: {}, | |||
| robotMsg: {}, | |||
| lastAction: "", | |||
| ws: {}, | |||
| debug: false | |||
| }; | |||
| }, | |||
| mutations: { | |||
| setHandleType(state, handleType) { | |||
| state.handleType = handleType; | |||
| }, | |||
| setLastReq(state, lastReq) { | |||
| state.lastReq = lastReq; | |||
| }, | |||
| setLastPerson(state, lastPerson) { | |||
| state.lastPerson = lastPerson; | |||
| }, | |||
| setProgressPerson(state, progressPerson) { | |||
| state.progressPerson = progressPerson; | |||
| }, | |||
| setLocalDevInfo(state, localDevInfo) { | |||
| state.localDevInfo = localDevInfo; | |||
| }, | |||
| setPad(state, pad) { | |||
| state.pad = pad; | |||
| }, | |||
| setRobotMsg(state, msg) { | |||
| state.robotMsg = msg; | |||
| }, | |||
| setLastAction(state, action) { | |||
| state.lastAction = action; | |||
| }, | |||
| setWs(state, ws) { | |||
| state.ws = ws; | |||
| }, | |||
| setDebug(state, debug) { | |||
| state.debug = debug; | |||
| } | |||
| } | |||
| }); | |||
| export default store; | |||
| @@ -0,0 +1,24 @@ | |||
| $base-width: 540; | |||
| $base-height: 960; | |||
| $border-radius: 4px; | |||
| @function vw($px) { | |||
| @return $px / $base-width * 100vw; | |||
| } | |||
| @function vh($px) { | |||
| @return $px / $base-height * 100vh; | |||
| } | |||
| @function vmax($px) { | |||
| @return $px / $base-height * 100vh; | |||
| } | |||
| @function vmin($px) { | |||
| @return $px / $base-width * 100vw; | |||
| } | |||
| @function rem($px) { | |||
| @return $px / $base-width * 100vw; | |||
| } | |||
| @@ -0,0 +1,149 @@ | |||
| import Config from "../config"; | |||
| import { genTimeText } from "./handleTtsFun"; | |||
| export const getTimeState2 = () => { | |||
| let timeNow = new Date(); | |||
| let hours = timeNow.getHours(); | |||
| // 设置默认文字 | |||
| let text = ""; | |||
| if (hours > 18 && hours <= 24) { | |||
| text = "挺晚了,您今天辛苦了!"; | |||
| } | |||
| // 返回当前时间段对应的状态 | |||
| return text; | |||
| }; | |||
| export const genResp = (tag) => { | |||
| let result = []; | |||
| if (genTimeText() != "") { | |||
| result.push(genTimeText()); | |||
| } | |||
| if (getTimeState2() != "") { | |||
| result.push(getTimeState2()); | |||
| } | |||
| if (!(tag == Config.userType.courier || tag == Config.userType.blackRole)) { | |||
| return result; | |||
| } | |||
| return []; | |||
| }; | |||
| export const nameShort = (name) => { | |||
| let str = ""; | |||
| if (/^[a-zA-Z]+$/.test(name)) { | |||
| str = name; | |||
| } else { | |||
| str += name !== "" ? name.substr(0, 1) : ""; | |||
| } | |||
| return str; | |||
| }; | |||
| export const getRandomOne = (array) => { | |||
| let randNum = Math.floor(Math.random() * array.length); | |||
| let str = array[randNum]; | |||
| return str; | |||
| }; | |||
| export const getSevenDays = (weekNum) => { | |||
| let oneDay = 24 * 3600 * 1000; | |||
| let oneWeek = 7 * oneDay; | |||
| let base = Date.parse(new Date()) + oneWeek * weekNum; // 转换为时间戳 | |||
| let date = new Date(base); | |||
| let weekDay = date.getDay(); //本周第几天 | |||
| let timeData = {}; | |||
| let days = []; | |||
| for (let i = 0; i < 7; i++) { | |||
| let time = base - (weekDay - i) * oneDay; | |||
| let now = new Date(time); | |||
| let day = {}; | |||
| day.year = now.getFullYear(); | |||
| day.month = now.getMonth() + 1; | |||
| day.monthDay = now.getDate(); //本月多少号 | |||
| day.dateStr = day.year + "-" + day.month + "-" + day.monthDay; | |||
| day.en = getDayEn(now.getDay()); | |||
| days.push(day); | |||
| if (weekDay - i == 0) { | |||
| timeData.selectDay = day; | |||
| } | |||
| } | |||
| timeData.days = days; | |||
| return timeData; | |||
| }; | |||
| function getDayEn(day) { | |||
| if (day == 0) { | |||
| return "SUN"; | |||
| } else if (day == 1) { | |||
| return "MON"; | |||
| } else if (day == 2) { | |||
| return "TUE"; | |||
| } else if (day == 3) { | |||
| return "WED"; | |||
| } else if (day == 4) { | |||
| return "THU"; | |||
| } else if (day == 5) { | |||
| return "FRI"; | |||
| } else if (day == 6) { | |||
| return "SAT"; | |||
| } | |||
| return ""; | |||
| } | |||
| export const containStr = (array, text) => { | |||
| for (var i = 0; i < array.length; i++) { | |||
| if (text.indexOf(array[i]) > -1) { | |||
| return text.indexOf(array[i]); | |||
| } | |||
| } | |||
| return -1; | |||
| }; | |||
| export const containStrStr = (array, text) => { | |||
| for (var i = 0; i < array.length; i++) { | |||
| if (text.indexOf(array[i]) > -1) { | |||
| return array[i]; | |||
| } | |||
| } | |||
| return -1; | |||
| }; | |||
| export const containObjByCode = (array, code) => { | |||
| for (var i = 0; i < array.length; i++) { | |||
| if (array[i]["robotCode"] === code) { | |||
| return array[i]; | |||
| } | |||
| } | |||
| return null; | |||
| }; | |||
| export const containStrIndex = (array, text) => { | |||
| for (var i = 0; i < array.length; i++) { | |||
| if (text.indexOf(array[i]) > -1) { | |||
| return i; | |||
| } | |||
| } | |||
| return -1; | |||
| }; | |||
| export const arrayComm = (array) => { | |||
| array.sort(); | |||
| let ary = []; | |||
| let index = 0; | |||
| if (array.length > 1) { | |||
| for (let i = 0; i < array.length; i++) { | |||
| let tmp = array[i].split("-"); | |||
| let temp = { start: tmp[0] }; | |||
| for (let j = i + 1; j < array.length; j++) { | |||
| let tmp2 = array[j].split("-"); | |||
| if ((tmp[1] = tmp2[0])) { | |||
| temp.end = tmp2[1]; | |||
| } else { | |||
| ary[index++] = [temp.start + "-" + temp.end]; | |||
| i = j; | |||
| } | |||
| } | |||
| } | |||
| return ary; | |||
| } else { | |||
| return array; | |||
| } | |||
| }; | |||