Commit f92fd6ac57410277e647cc75523c503ea9fb90a7
1 parent
ce5e1a2a
开发我的小区下的功能
Showing
64 changed files
with
8758 additions
and
0 deletions
public/index.html
0 → 100644
| 1 | +<!DOCTYPE html> | |
| 2 | +<html lang="en"> | |
| 3 | +<head> | |
| 4 | + <meta charset="utf-8"> | |
| 5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
| 6 | + <meta name="viewport" content="width=device-width,initial-scale=1.0"> | |
| 7 | + <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | |
| 8 | + <title><%= htmlWebpackPlugin.options.title %></title> | |
| 9 | + <!-- 全局脚本引入 --> | |
| 10 | + <script> | |
| 11 | + // 脚本加载错误处理 | |
| 12 | + window.scriptLoadErrors = []; | |
| 13 | + | |
| 14 | + function handleScriptError(scriptName, error) { | |
| 15 | + console.error('脚本加载失败:', scriptName, error); | |
| 16 | + window.scriptLoadErrors.push({ name: scriptName, error: error.message }); | |
| 17 | + } | |
| 18 | + </script> | |
| 19 | + <script src="https://map.qq.com/api/gljs?v=1.exp&key="></script> | |
| 20 | + | |
| 21 | + <!-- Qs 库 - 主 CDN --> | |
| 22 | + <script src="https://cdn.bootcdn.net/ajax/libs/qs/6.10.3/qs.min.js" | |
| 23 | + onerror="handleScriptError('Qs-bootcdn', new Error('bootcdn 加载失败'))"></script> | |
| 24 | + <script src="/js/jessibuca/jessibuca.js"></script> | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + <!-- 可以在这里添加更多全局脚本 --> | |
| 29 | +</head> | |
| 30 | +<body> | |
| 31 | + <noscript> | |
| 32 | + <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | |
| 33 | + </noscript> | |
| 34 | + <div id="app"></div> | |
| 35 | + <!-- built files will be auto injected --> | |
| 36 | +</body> | |
| 37 | + | |
| 38 | +</html> | |
| 0 | 39 | \ No newline at end of file | ... | ... |
public/js/jessibuca/jessibuca.d.ts
0 → 100644
| 1 | +declare namespace Jessibuca { | |
| 2 | + | |
| 3 | + /** 超时信息 */ | |
| 4 | + enum TIMEOUT { | |
| 5 | + /** 当play()的时候,如果没有数据返回 */ | |
| 6 | + loadingTimeout = 'loadingTimeout', | |
| 7 | + /** 当播放过程中,如果超过timeout之后没有数据渲染 */ | |
| 8 | + delayTimeout = 'delayTimeout', | |
| 9 | + } | |
| 10 | + | |
| 11 | + /** 错误信息 */ | |
| 12 | + enum ERROR { | |
| 13 | + /** 播放错误,url 为空的时候,调用 play 方法 */ | |
| 14 | + playError = 'playError', | |
| 15 | + /** http 请求失败 */ | |
| 16 | + fetchError = 'fetchError', | |
| 17 | + /** websocket 请求失败 */ | |
| 18 | + websocketError = 'websocketError', | |
| 19 | + /** webcodecs 解码 h265 失败 */ | |
| 20 | + webcodecsH265NotSupport = 'webcodecsH265NotSupport', | |
| 21 | + /** mediaSource 解码 h265 失败 */ | |
| 22 | + mediaSourceH265NotSupport = 'mediaSourceH265NotSupport', | |
| 23 | + /** wasm 解码失败 */ | |
| 24 | + wasmDecodeError = 'wasmDecodeError', | |
| 25 | + } | |
| 26 | + | |
| 27 | + interface Config { | |
| 28 | + /** | |
| 29 | + * 播放器容器 | |
| 30 | + * * 若为 string ,则底层调用的是 document.getElementById('id') | |
| 31 | + * */ | |
| 32 | + container: HTMLElement | string; | |
| 33 | + /** | |
| 34 | + * 设置最大缓冲时长,单位秒,播放器会自动消除延迟 | |
| 35 | + */ | |
| 36 | + videoBuffer?: number; | |
| 37 | + /** | |
| 38 | + * worker地址 | |
| 39 | + * * 默认引用的是根目录下面的decoder.js文件 ,decoder.js 与 decoder.wasm文件必须是放在同一个目录下面。 */ | |
| 40 | + decoder?: string; | |
| 41 | + /** | |
| 42 | + * 是否不使用离屏模式(提升渲染能力) | |
| 43 | + */ | |
| 44 | + forceNoOffscreen?: boolean; | |
| 45 | + /** | |
| 46 | + * 是否开启当页面的'visibilityState'变为'hidden'的时候,自动暂停播放。 | |
| 47 | + */ | |
| 48 | + hiddenAutoPause?: boolean; | |
| 49 | + /** | |
| 50 | + * 是否有音频,如果设置`false`,则不对音频数据解码,提升性能。 | |
| 51 | + */ | |
| 52 | + hasAudio?: boolean; | |
| 53 | + /** | |
| 54 | + * 设置旋转角度,只支持,0(默认),180,270 三个值 | |
| 55 | + */ | |
| 56 | + rotate?: boolean; | |
| 57 | + /** | |
| 58 | + * 1. 当为`true`的时候:视频画面做等比缩放后,高或宽对齐canvas区域,画面不被拉伸,但有黑边。 等同于 `setScaleMode(1)` | |
| 59 | + * 2. 当为`false`的时候:视频画面完全填充canvas区域,画面会被拉伸。等同于 `setScaleMode(0)` | |
| 60 | + */ | |
| 61 | + isResize?: boolean; | |
| 62 | + /** | |
| 63 | + * 1. 当为`true`的时候:视频画面做等比缩放后,完全填充canvas区域,画面不被拉伸,没有黑边,但画面显示不全。等同于 `setScaleMode(2)` | |
| 64 | + */ | |
| 65 | + isFullResize?: boolean; | |
| 66 | + /** | |
| 67 | + * 1. 当为`true`的时候:ws协议不检验是否以.flv为依据,进行协议解析。 | |
| 68 | + */ | |
| 69 | + isFlv?: boolean; | |
| 70 | + /** | |
| 71 | + * 是否开启控制台调试打 | |
| 72 | + */ | |
| 73 | + debug?: boolean; | |
| 74 | + /** | |
| 75 | + * 1. 设置超时时长, 单位秒 | |
| 76 | + * 2. 在连接成功之前(loading)和播放中途(heart),如果超过设定时长无数据返回,则回调timeout事件 | |
| 77 | + */ | |
| 78 | + timeout?: number; | |
| 79 | + /** | |
| 80 | + * 1. 设置超时时长, 单位秒 | |
| 81 | + * 2. 在连接成功之前,如果超过设定时长无数据返回,则回调timeout事件 | |
| 82 | + */ | |
| 83 | + heartTimeout?: number; | |
| 84 | + /** | |
| 85 | + * 1. 设置超时时长, 单位秒 | |
| 86 | + * 2. 在连接成功之前,如果超过设定时长无数据返回,则回调timeout事件 | |
| 87 | + */ | |
| 88 | + loadingTimeout?: number; | |
| 89 | + /** | |
| 90 | + * 是否支持屏幕的双击事件,触发全屏,取消全屏事件 | |
| 91 | + */ | |
| 92 | + supportDblclickFullscreen?: boolean; | |
| 93 | + /** | |
| 94 | + * 是否显示网 | |
| 95 | + */ | |
| 96 | + showBandwidth?: boolean; | |
| 97 | + /** | |
| 98 | + * 配置操作按钮 | |
| 99 | + */ | |
| 100 | + operateBtns?: { | |
| 101 | + /** 是否显示全屏按钮 */ | |
| 102 | + fullscreen?: boolean; | |
| 103 | + /** 是否显示截图按钮 */ | |
| 104 | + screenshot?: boolean; | |
| 105 | + /** 是否显示播放暂停按钮 */ | |
| 106 | + play?: boolean; | |
| 107 | + /** 是否显示声音按钮 */ | |
| 108 | + audio?: boolean; | |
| 109 | + /** 是否显示录制按 */ | |
| 110 | + record?: boolean; | |
| 111 | + }; | |
| 112 | + /** | |
| 113 | + * 开启屏幕常亮,在手机浏览器上, canvas标签渲染视频并不会像video标签那样保持屏幕常亮 | |
| 114 | + */ | |
| 115 | + keepScreenOn?: boolean; | |
| 116 | + /** | |
| 117 | + * 是否开启声音,默认是关闭声音播放的 | |
| 118 | + */ | |
| 119 | + isNotMute?: boolean; | |
| 120 | + /** | |
| 121 | + * 加载过程中文案 | |
| 122 | + */ | |
| 123 | + loadingText?: string; | |
| 124 | + /** | |
| 125 | + * 背景图片 | |
| 126 | + */ | |
| 127 | + background?: string; | |
| 128 | + /** | |
| 129 | + * 是否开启MediaSource硬解码 | |
| 130 | + * * 视频编码只支持H.264视频(Safari on iOS不支持) | |
| 131 | + * * 不支持 forceNoOffscreen 为 false (开启离屏渲染) | |
| 132 | + */ | |
| 133 | + useMSE?: boolean; | |
| 134 | + /** | |
| 135 | + * 是否开启Webcodecs硬解码 | |
| 136 | + * * 视频编码只支持H.264视频 (需在chrome 94版本以上,需要https或者localhost环境) | |
| 137 | + * * 支持 forceNoOffscreen 为 false (开启离屏渲染) | |
| 138 | + * */ | |
| 139 | + useWCS?: boolean; | |
| 140 | + /** | |
| 141 | + * 是否开启键盘快捷键 | |
| 142 | + * 目前支持的键盘快捷键有:esc -> 退出全屏;arrowUp -> 声音增加;arrowDown -> 声音减少; | |
| 143 | + */ | |
| 144 | + hotKey?: boolean; | |
| 145 | + /** | |
| 146 | + * 在使用MSE或者Webcodecs 播放H265的时候,是否自动降级到wasm模式。 | |
| 147 | + * 设置为false 则直接关闭播放,抛出Error 异常,设置为true 则会自动切换成wasm模式播放。 | |
| 148 | + */ | |
| 149 | + autoWasm?: boolean; | |
| 150 | + /** | |
| 151 | + * heartTimeout 心跳超时之后自动再播放,不再抛出异常,而直接重新播放视频地址。 | |
| 152 | + */ | |
| 153 | + heartTimeoutReplay?: boolean, | |
| 154 | + /** | |
| 155 | + * heartTimeoutReplay 从试次数,超过之后,不再自动播放 | |
| 156 | + */ | |
| 157 | + heartTimeoutReplayTimes?: number, | |
| 158 | + /** | |
| 159 | + * loadingTimeout loading之后自动再播放,不再抛出异常,而直接重新播放视频地址。 | |
| 160 | + */ | |
| 161 | + loadingTimeoutReplay?: boolean, | |
| 162 | + /** | |
| 163 | + * heartTimeoutReplay 从试次数,超过之后,不再自动播放 | |
| 164 | + */ | |
| 165 | + loadingTimeoutReplayTimes?: number | |
| 166 | + /** | |
| 167 | + * wasm解码报错之后,不再抛出异常,而是直接重新播放视频地址。 | |
| 168 | + */ | |
| 169 | + wasmDecodeErrorReplay?: boolean, | |
| 170 | + /** | |
| 171 | + * https://github.com/langhuihui/jessibuca/issues/152 解决方案 | |
| 172 | + * 例如:WebGL图像预处理默认每次取4字节的数据,但是540x960分辨率下的U、V分量宽度是540/2=270不能被4整除,导致绿屏。 | |
| 173 | + */ | |
| 174 | + openWebglAlignment?: boolean | |
| 175 | + } | |
| 176 | +} | |
| 177 | + | |
| 178 | + | |
| 179 | +declare class Jessibuca { | |
| 180 | + | |
| 181 | + constructor(config?: Jessibuca.Config); | |
| 182 | + | |
| 183 | + /** | |
| 184 | + * 是否开启控制台调试打印 | |
| 185 | + @example | |
| 186 | + // 开启 | |
| 187 | + jessibuca.setDebug(true) | |
| 188 | + // 关闭 | |
| 189 | + jessibuca.setDebug(false) | |
| 190 | + */ | |
| 191 | + setDebug(flag: boolean): void; | |
| 192 | + | |
| 193 | + /** | |
| 194 | + * 静音 | |
| 195 | + @example | |
| 196 | + jessibuca.mute() | |
| 197 | + */ | |
| 198 | + mute(): void; | |
| 199 | + | |
| 200 | + /** | |
| 201 | + * 取消静音 | |
| 202 | + @example | |
| 203 | + jessibuca.cancelMute() | |
| 204 | + */ | |
| 205 | + cancelMute(): void; | |
| 206 | + | |
| 207 | + /** | |
| 208 | + * 留给上层用户操作来触发音频恢复的方法。 | |
| 209 | + * | |
| 210 | + * iPhone,chrome等要求自动播放时,音频必须静音,需要由一个真实的用户交互操作来恢复,不能使用代码。 | |
| 211 | + * | |
| 212 | + * https://developers.google.com/web/updates/2017/09/autoplay-policy-changes | |
| 213 | + */ | |
| 214 | + audioResume(): void; | |
| 215 | + | |
| 216 | + /** | |
| 217 | + * | |
| 218 | + * 设置超时时长, 单位秒 | |
| 219 | + * 在连接成功之前和播放中途,如果超过设定时长无数据返回,则回调timeout事件 | |
| 220 | + | |
| 221 | + @example | |
| 222 | + jessibuca.setTimeout(10) | |
| 223 | + | |
| 224 | + jessibuca.on('timeout',function(){ | |
| 225 | + // | |
| 226 | + }); | |
| 227 | + */ | |
| 228 | + setTimeout(): void; | |
| 229 | + | |
| 230 | + /** | |
| 231 | + * @param mode | |
| 232 | + * 0 视频画面完全填充canvas区域,画面会被拉伸 等同于参数 `isResize` 为false | |
| 233 | + * | |
| 234 | + * 1 视频画面做等比缩放后,高或宽对齐canvas区域,画面不被拉伸,但有黑边 等同于参数 `isResize` 为true | |
| 235 | + * | |
| 236 | + * 2 视频画面做等比缩放后,完全填充canvas区域,画面不被拉伸,没有黑边,但画面显示不全 等同于参数 `isFullResize` 为true | |
| 237 | + @example | |
| 238 | + jessibuca.setScaleMode(0) | |
| 239 | + | |
| 240 | + jessibuca.setScaleMode(1) | |
| 241 | + | |
| 242 | + jessibuca.setScaleMode(2) | |
| 243 | + */ | |
| 244 | + setScaleMode(mode: number): void; | |
| 245 | + | |
| 246 | + /** | |
| 247 | + * 暂停播放 | |
| 248 | + * | |
| 249 | + * 可以在pause 之后,再调用 `play()`方法就继续播放之前的流。 | |
| 250 | + @example | |
| 251 | + jessibuca.pause().then(()=>{ | |
| 252 | + console.log('pause success') | |
| 253 | + | |
| 254 | + jessibuca.play().then(()=>{ | |
| 255 | + | |
| 256 | + }).catch((e)=>{ | |
| 257 | + | |
| 258 | + }) | |
| 259 | + | |
| 260 | + }).catch((e)=>{ | |
| 261 | + console.log('pause error',e); | |
| 262 | + }) | |
| 263 | + */ | |
| 264 | + pause(): Promise<void>; | |
| 265 | + | |
| 266 | + /** | |
| 267 | + * 关闭视频,不释放底层资源 | |
| 268 | + @example | |
| 269 | + jessibuca.close(); | |
| 270 | + */ | |
| 271 | + close(): void; | |
| 272 | + | |
| 273 | + /** | |
| 274 | + * 关闭视频,释放底层资源 | |
| 275 | + @example | |
| 276 | + jessibuca.destroy() | |
| 277 | + */ | |
| 278 | + destroy(): void; | |
| 279 | + | |
| 280 | + /** | |
| 281 | + * 清理画布为黑色背景 | |
| 282 | + @example | |
| 283 | + jessibuca.clearView() | |
| 284 | + */ | |
| 285 | + clearView(): void; | |
| 286 | + | |
| 287 | + /** | |
| 288 | + * 播放视频 | |
| 289 | + @example | |
| 290 | + | |
| 291 | + jessibuca.play('url').then(()=>{ | |
| 292 | + console.log('play success') | |
| 293 | + }).catch((e)=>{ | |
| 294 | + console.log('play error',e) | |
| 295 | + }) | |
| 296 | + // | |
| 297 | + jessibuca.play() | |
| 298 | + */ | |
| 299 | + play(url?: string): Promise<void>; | |
| 300 | + | |
| 301 | + /** | |
| 302 | + * 重新调整视图大小 | |
| 303 | + */ | |
| 304 | + resize(): void; | |
| 305 | + | |
| 306 | + /** | |
| 307 | + * 设置最大缓冲时长,单位秒,播放器会自动消除延迟。 | |
| 308 | + * | |
| 309 | + * 等同于 `videoBuffer` 参数。 | |
| 310 | + * | |
| 311 | + @example | |
| 312 | + // 设置 200ms 缓冲 | |
| 313 | + jessibuca.setBufferTime(0.2) | |
| 314 | + */ | |
| 315 | + setBufferTime(time: number): void; | |
| 316 | + | |
| 317 | + /** | |
| 318 | + * 设置旋转角度,只支持,0(默认) ,180,270 三个值。 | |
| 319 | + * | |
| 320 | + * > 可用于实现监控画面小窗和全屏效果,由于iOS没有全屏API,此方法可以模拟页面内全屏效果而且多端效果一致。 * | |
| 321 | + @example | |
| 322 | + jessibuca.setRotate(0) | |
| 323 | + | |
| 324 | + jessibuca.setRotate(90) | |
| 325 | + | |
| 326 | + jessibuca.setRotate(270) | |
| 327 | + */ | |
| 328 | + setRotate(deg: number): void; | |
| 329 | + | |
| 330 | + /** | |
| 331 | + * | |
| 332 | + * 设置音量大小,取值0 — 1 | |
| 333 | + * | |
| 334 | + * > 区别于 mute 和 cancelMute 方法,虽然设置setVolume(0) 也能达到 mute方法,但是mute 方法是不调用底层播放音频的,能提高性能。而setVolume(0)只是把声音设置为0 ,以达到效果。 | |
| 335 | + * @param volume 当为0时,完全无声;当为1时,最大音量,默认值 | |
| 336 | + @example | |
| 337 | + jessibuca.setVolume(0.2) | |
| 338 | + | |
| 339 | + jessibuca.setVolume(0) | |
| 340 | + | |
| 341 | + jessibuca.setVolume(1) | |
| 342 | + */ | |
| 343 | + setVolume(volume: number): void; | |
| 344 | + | |
| 345 | + /** | |
| 346 | + * 返回是否加载完毕 | |
| 347 | + @example | |
| 348 | + var result = jessibuca.hasLoaded() | |
| 349 | + console.log(result) // true | |
| 350 | + */ | |
| 351 | + hasLoaded(): boolean; | |
| 352 | + | |
| 353 | + /** | |
| 354 | + * 开启屏幕常亮,在手机浏览器上, canvas标签渲染视频并不会像video标签那样保持屏幕常亮。 | |
| 355 | + * H5目前在chrome\edge 84, android chrome 84及以上有原生亮屏API, 需要是https页面 | |
| 356 | + * 其余平台为模拟实现,此时为兼容实现,并不保证所有浏览器都支持 | |
| 357 | + @example | |
| 358 | + jessibuca.setKeepScreenOn() | |
| 359 | + */ | |
| 360 | + setKeepScreenOn(): boolean; | |
| 361 | + | |
| 362 | + /** | |
| 363 | + * 全屏(取消全屏)播放视频 | |
| 364 | + @example | |
| 365 | + jessibuca.setFullscreen(true) | |
| 366 | + // | |
| 367 | + jessibuca.setFullscreen(false) | |
| 368 | + */ | |
| 369 | + setFullscreen(flag: boolean): void; | |
| 370 | + | |
| 371 | + /** | |
| 372 | + * | |
| 373 | + * 截图,调用后弹出下载框保存截图 | |
| 374 | + * @param filename 可选参数, 保存的文件名, 默认 `时间戳` | |
| 375 | + * @param format 可选参数, 截图的格式,可选png或jpeg或者webp ,默认 `png` | |
| 376 | + * @param quality 可选参数, 当格式是jpeg或者webp时,压缩质量,取值0 ~ 1 ,默认 `0.92` | |
| 377 | + * @param type 可选参数, 可选download或者base64或者blob,默认`download` | |
| 378 | + | |
| 379 | + @example | |
| 380 | + | |
| 381 | + jessibuca.screenshot("test","png",0.5) | |
| 382 | + | |
| 383 | + const base64 = jessibuca.screenshot("test","png",0.5,'base64') | |
| 384 | + | |
| 385 | + const fileBlob = jessibuca.screenshot("test",'blob') | |
| 386 | + */ | |
| 387 | + screenshot(filename?: string, format?: string, quality?: number, type?: string): void; | |
| 388 | + | |
| 389 | + /** | |
| 390 | + * 开始录制。 | |
| 391 | + * @param fileName 可选,默认时间戳 | |
| 392 | + * @param fileType 可选,默认webm,支持webm 和mp4 格式 | |
| 393 | + | |
| 394 | + @example | |
| 395 | + jessibuca.startRecord('xxx','webm') | |
| 396 | + */ | |
| 397 | + startRecord(fileName: string, fileType: string): void; | |
| 398 | + | |
| 399 | + /** | |
| 400 | + * 暂停录制并下载。 | |
| 401 | + @example | |
| 402 | + jessibuca.stopRecordAndSave() | |
| 403 | + */ | |
| 404 | + stopRecordAndSave(): void; | |
| 405 | + | |
| 406 | + /** | |
| 407 | + * 返回是否正在播放中状态。 | |
| 408 | + @example | |
| 409 | + var result = jessibuca.isPlaying() | |
| 410 | + console.log(result) // true | |
| 411 | + */ | |
| 412 | + isPlaying(): boolean; | |
| 413 | + | |
| 414 | + /** | |
| 415 | + * 返回是否静音。 | |
| 416 | + @example | |
| 417 | + var result = jessibuca.isMute() | |
| 418 | + console.log(result) // true | |
| 419 | + */ | |
| 420 | + isMute(): boolean; | |
| 421 | + | |
| 422 | + /** | |
| 423 | + * 返回是否正在录制。 | |
| 424 | + @example | |
| 425 | + var result = jessibuca.isRecording() | |
| 426 | + console.log(result) // true | |
| 427 | + */ | |
| 428 | + isRecording(): boolean; | |
| 429 | + | |
| 430 | + | |
| 431 | + /** | |
| 432 | + * 监听 jessibuca 初始化事件 | |
| 433 | + * @example | |
| 434 | + * jessibuca.on("load",function(){console.log('load')}) | |
| 435 | + */ | |
| 436 | + on(event: 'load', callback: () => void): void; | |
| 437 | + | |
| 438 | + /** | |
| 439 | + * 视频播放持续时间,单位ms | |
| 440 | + * @example | |
| 441 | + * jessibuca.on('timeUpdate',function (ts) {console.log('timeUpdate',ts);}) | |
| 442 | + */ | |
| 443 | + on(event: 'timeUpdate', callback: () => void): void; | |
| 444 | + | |
| 445 | + /** | |
| 446 | + * 当解析出视频信息时回调,2个回调参数 | |
| 447 | + * @example | |
| 448 | + * jessibuca.on("videoInfo",function(data){console.log('width:',data.width,'height:',data.width)}) | |
| 449 | + */ | |
| 450 | + on(event: 'videoInfo', callback: (data: { | |
| 451 | + /** 视频宽 */ | |
| 452 | + width: number; | |
| 453 | + /** 视频高 */ | |
| 454 | + height: number; | |
| 455 | + }) => void): void; | |
| 456 | + | |
| 457 | + /** | |
| 458 | + * 当解析出音频信息时回调,2个回调参数 | |
| 459 | + * @example | |
| 460 | + * jessibuca.on("audioInfo",function(data){console.log('numOfChannels:',data.numOfChannels,'sampleRate',data.sampleRate)}) | |
| 461 | + */ | |
| 462 | + on(event: 'audioInfo', callback: (data: { | |
| 463 | + /** 声频通道 */ | |
| 464 | + numOfChannels: number; | |
| 465 | + /** 采样率 */ | |
| 466 | + sampleRate: number; | |
| 467 | + }) => void): void; | |
| 468 | + | |
| 469 | + /** | |
| 470 | + * 信息,包含错误信息 | |
| 471 | + * @example | |
| 472 | + * jessibuca.on("log",function(data){console.log('data:',data)}) | |
| 473 | + */ | |
| 474 | + on(event: 'log', callback: () => void): void; | |
| 475 | + | |
| 476 | + /** | |
| 477 | + * 错误信息 | |
| 478 | + * @example | |
| 479 | + * jessibuca.on("error",function(error){ | |
| 480 | + if(error === Jessibuca.ERROR.fetchError){ | |
| 481 | + // | |
| 482 | + } | |
| 483 | + else if(error === Jessibuca.ERROR.webcodecsH265NotSupport){ | |
| 484 | + // | |
| 485 | + } | |
| 486 | + console.log('error:',error) | |
| 487 | + }) | |
| 488 | + */ | |
| 489 | + on(event: 'error', callback: (err: Jessibuca.ERROR) => void): void; | |
| 490 | + | |
| 491 | + /** | |
| 492 | + * 当前网速, 单位KB 每秒1次, | |
| 493 | + * @example | |
| 494 | + * jessibuca.on("kBps",function(data){console.log('kBps:',data)}) | |
| 495 | + */ | |
| 496 | + on(event: 'kBps', callback: (value: number) => void): void; | |
| 497 | + | |
| 498 | + /** | |
| 499 | + * 渲染开始 | |
| 500 | + * @example | |
| 501 | + * jessibuca.on("start",function(){console.log('start render')}) | |
| 502 | + */ | |
| 503 | + on(event: 'start', callback: () => void): void; | |
| 504 | + | |
| 505 | + /** | |
| 506 | + * 当设定的超时时间内无数据返回,则回调 | |
| 507 | + * @example | |
| 508 | + * jessibuca.on("timeout",function(error){console.log('timeout:',error)}) | |
| 509 | + */ | |
| 510 | + on(event: 'timeout', callback: (error: Jessibuca.TIMEOUT) => void): void; | |
| 511 | + | |
| 512 | + /** | |
| 513 | + * 当play()的时候,如果没有数据返回,则回调 | |
| 514 | + * @example | |
| 515 | + * jessibuca.on("loadingTimeout",function(){console.log('timeout')}) | |
| 516 | + */ | |
| 517 | + on(event: 'loadingTimeout', callback: () => void): void; | |
| 518 | + | |
| 519 | + /** | |
| 520 | + * 当播放过程中,如果超过timeout之后没有数据渲染,则抛出异常。 | |
| 521 | + * @example | |
| 522 | + * jessibuca.on("delayTimeout",function(){console.log('timeout')}) | |
| 523 | + */ | |
| 524 | + on(event: 'delayTimeout', callback: () => void): void; | |
| 525 | + | |
| 526 | + /** | |
| 527 | + * 当前是否全屏 | |
| 528 | + * @example | |
| 529 | + * jessibuca.on("fullscreen",function(flag){console.log('is fullscreen',flag)}) | |
| 530 | + */ | |
| 531 | + on(event: 'fullscreen', callback: () => void): void; | |
| 532 | + | |
| 533 | + /** | |
| 534 | + * 触发播放事件 | |
| 535 | + * @example | |
| 536 | + * jessibuca.on("play",function(flag){console.log('play')}) | |
| 537 | + */ | |
| 538 | + on(event: 'play', callback: () => void): void; | |
| 539 | + | |
| 540 | + /** | |
| 541 | + * 触发暂停事件 | |
| 542 | + * @example | |
| 543 | + * jessibuca.on("pause",function(flag){console.log('pause')}) | |
| 544 | + */ | |
| 545 | + on(event: 'pause', callback: () => void): void; | |
| 546 | + | |
| 547 | + /** | |
| 548 | + * 触发声音事件,返回boolean值 | |
| 549 | + * @example | |
| 550 | + * jessibuca.on("mute",function(flag){console.log('is mute',flag)}) | |
| 551 | + */ | |
| 552 | + on(event: 'mute', callback: () => void): void; | |
| 553 | + | |
| 554 | + /** | |
| 555 | + * 流状态统计,流开始播放后回调,每秒1次。 | |
| 556 | + * @example | |
| 557 | + * jessibuca.on("stats",function(s){console.log("stats is",s)}) | |
| 558 | + */ | |
| 559 | + on(event: 'stats', callback: (stats: { | |
| 560 | + /** 当前缓冲区时长,单位毫秒 */ | |
| 561 | + buf: number; | |
| 562 | + /** 当前视频帧率 */ | |
| 563 | + fps: number; | |
| 564 | + /** 当前音频码率,单位byte */ | |
| 565 | + abps: number; | |
| 566 | + /** 当前视频码率,单位byte */ | |
| 567 | + vbps: number; | |
| 568 | + /** 当前视频帧pts,单位毫秒 */ | |
| 569 | + ts: number; | |
| 570 | + }) => void): void; | |
| 571 | + | |
| 572 | + /** | |
| 573 | + * 渲染性能统计,流开始播放后回调,每秒1次。 | |
| 574 | + * @param performance 0: 表示卡顿,1: 表示流畅,2: 表示非常流程 | |
| 575 | + * @example | |
| 576 | + * jessibuca.on("performance",function(performance){console.log("performance is",performance)}) | |
| 577 | + */ | |
| 578 | + on(event: 'performance', callback: (performance: 0 | 1 | 2) => void): void; | |
| 579 | + | |
| 580 | + /** | |
| 581 | + * 录制开始的事件 | |
| 582 | + | |
| 583 | + * @example | |
| 584 | + * jessibuca.on("recordStart",function(){console.log("record start")}) | |
| 585 | + */ | |
| 586 | + on(event: 'recordStart', callback: () => void): void; | |
| 587 | + | |
| 588 | + /** | |
| 589 | + * 录制结束的事件 | |
| 590 | + | |
| 591 | + * @example | |
| 592 | + * jessibuca.on("recordEnd",function(){console.log("record end")}) | |
| 593 | + */ | |
| 594 | + on(event: 'recordEnd', callback: () => void): void; | |
| 595 | + | |
| 596 | + /** | |
| 597 | + * 录制的时候,返回的录制时长,1s一次 | |
| 598 | + | |
| 599 | + * @example | |
| 600 | + * jessibuca.on("recordingTimestamp",function(timestamp){console.log("recordingTimestamp is",timestamp)}) | |
| 601 | + */ | |
| 602 | + on(event: 'recordingTimestamp', callback: (timestamp: number) => void): void; | |
| 603 | + | |
| 604 | + /** | |
| 605 | + * 监听调用play方法 经过 初始化-> 网络请求-> 解封装 -> 解码 -> 渲染 一系列过程的时间消耗 | |
| 606 | + * @param event | |
| 607 | + * @param callback | |
| 608 | + */ | |
| 609 | + on(event: 'playToRenderTimes', callback: (times: { | |
| 610 | + playInitStart: number, // 1 初始化 | |
| 611 | + playStart: number, // 2 初始化 | |
| 612 | + streamStart: number, // 3 网络请求 | |
| 613 | + streamResponse: number, // 4 网络请求 | |
| 614 | + demuxStart: number, // 5 解封装 | |
| 615 | + decodeStart: number, // 6 解码 | |
| 616 | + videoStart: number, // 7 渲染 | |
| 617 | + playTimestamp: number,// playStart- playInitStart | |
| 618 | + streamTimestamp: number,// streamStart - playStart | |
| 619 | + streamResponseTimestamp: number,// streamResponse - streamStart | |
| 620 | + demuxTimestamp: number, // demuxStart - streamResponse | |
| 621 | + decodeTimestamp: number, // decodeStart - demuxStart | |
| 622 | + videoTimestamp: number,// videoStart - decodeStart | |
| 623 | + allTimestamp: number // videoStart - playInitStart | |
| 624 | + }) => void): void | |
| 625 | + | |
| 626 | + /** | |
| 627 | + * 监听方法 | |
| 628 | + * | |
| 629 | + @example | |
| 630 | + | |
| 631 | + jessibuca.on("load",function(){console.log('load')}) | |
| 632 | + */ | |
| 633 | + on(event: string, callback: Function): void; | |
| 634 | + | |
| 635 | +} | |
| 636 | + | |
| 637 | +export default Jessibuca; | ... | ... |
public/js/jessibuca/jessibuca.js
0 → 100644
No preview for this file type
public/js/jessibuca/renderer.js
0 → 100644
| 1 | +!(function () { | |
| 2 | + /** | |
| 3 | + * @param opt | |
| 4 | + * container: DOM 容器 | |
| 5 | + * contextOptions: | |
| 6 | + * videoBuffer: | |
| 7 | + * forceNoGL: | |
| 8 | + * isNotMute: | |
| 9 | + * decoder: | |
| 10 | + * @constructor | |
| 11 | + */ | |
| 12 | + function Jessibuca(opt) { | |
| 13 | + this._opt = opt; | |
| 14 | + | |
| 15 | + if (typeof opt.container === "string") { | |
| 16 | + this._opt.container = document.getElementById(opt.container); | |
| 17 | + } | |
| 18 | + if (!this._opt.container) { | |
| 19 | + throw new Error('Jessibuca need container option'); | |
| 20 | + return; | |
| 21 | + } | |
| 22 | + | |
| 23 | + this._canvasElement = document.createElement("canvas"); | |
| 24 | + this._canvasElement.style.position = "absolute"; | |
| 25 | + this._canvasElement.style.top = 0; | |
| 26 | + this._canvasElement.style.left = 0; | |
| 27 | + this._opt.container.appendChild(this._canvasElement); | |
| 28 | + this._container = this._opt.container; | |
| 29 | + this._container.style.overflow = "hidden"; | |
| 30 | + this._containerOldPostion = { | |
| 31 | + position: this._container.style.position, | |
| 32 | + top: this._container.style.top, | |
| 33 | + left: this._container.style.left, | |
| 34 | + width: this._container.style.width, | |
| 35 | + height: this._container.style.height | |
| 36 | + } | |
| 37 | + if (this._containerOldPostion.position != "absolute") { | |
| 38 | + this._container.style.position = "relative" | |
| 39 | + } | |
| 40 | + this._opt.videoBuffer = opt.videoBuffer || 0; | |
| 41 | + this._opt.text = opt.text || ''; | |
| 42 | + // | |
| 43 | + this._opt.isResize = opt.isResize === false ? opt.isResize : true; | |
| 44 | + this._opt.isFullResize = opt.isFullResize === true ? opt.isFullResize : false; | |
| 45 | + this._opt.isDebug = opt.debug === true; | |
| 46 | + this._opt.timeout = typeof opt.timeout === 'number' ? opt.timeout : 30; | |
| 47 | + this._opt.supportDblclickFullscreen = opt.supportDblclickFullscreen === true; | |
| 48 | + this._opt.showBandwidth = opt.showBandwidth === true; | |
| 49 | + this._opt.operateBtns = Object.assign({ | |
| 50 | + fullscreen: false, | |
| 51 | + screenshot: false, | |
| 52 | + play: false, | |
| 53 | + audio: false | |
| 54 | + }, opt.operateBtns || {}); | |
| 55 | + this._opt.keepScreenOn = opt.keepScreenOn === true; | |
| 56 | + | |
| 57 | + if (!opt.forceNoGL) this._initContextGL(); | |
| 58 | + this._audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| 59 | + this._audioEnabled(true); | |
| 60 | + if (!opt.isNotMute) this._audioEnabled(false); | |
| 61 | + if (this._contextGL) { | |
| 62 | + this._initProgram(); | |
| 63 | + this._initBuffers(); | |
| 64 | + this._initTextures(); | |
| 65 | + } | |
| 66 | + this._onresize = () => this.resize(); | |
| 67 | + this._onfullscreenchange = () => this._fullscreenchange(); | |
| 68 | + window.addEventListener("resize", this._onresize); | |
| 69 | + document.addEventListener('fullscreenchange', this._onfullscreenchange); | |
| 70 | + this._decoderWorker = new Worker(opt.decoder || 'ff.js') | |
| 71 | + var _this = this; | |
| 72 | + this._hasLoaded = false; | |
| 73 | + this._stats = { | |
| 74 | + buf: 0, | |
| 75 | + fps: 0, | |
| 76 | + abps: '', | |
| 77 | + vbps: '', | |
| 78 | + ts: '' | |
| 79 | + }; | |
| 80 | + | |
| 81 | + if (this._opt.supportDblclickFullscreen) { | |
| 82 | + this._canvasElement.addEventListener('dblclick', function () { | |
| 83 | + _this.fullscreen = !_this.fullscreen; | |
| 84 | + }, false); | |
| 85 | + } | |
| 86 | + this.onPlay = noop; | |
| 87 | + this.onPause = noop; | |
| 88 | + this.onRecord = noop; | |
| 89 | + this.onFullscreen = noop; | |
| 90 | + this.onMute = noop; | |
| 91 | + this.onLoad = noop; | |
| 92 | + this.onLog = noop; | |
| 93 | + this.onError = noop; | |
| 94 | + this.onTimeUpdate = noop; | |
| 95 | + this.onInitSize = noop; | |
| 96 | + this._onMessage(); | |
| 97 | + this._initDom(); | |
| 98 | + this._initStatus(); | |
| 99 | + this._initEventListener(); | |
| 100 | + this._hideBtns(); | |
| 101 | + // | |
| 102 | + this._initWakeLock(); | |
| 103 | + this._enableWakeLock(); | |
| 104 | + }; | |
| 105 | + | |
| 106 | + function noop() { | |
| 107 | + | |
| 108 | + } | |
| 109 | + | |
| 110 | + Jessibuca.prototype._initDom = function () { | |
| 111 | + var playBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAARVJREFUSMe9laEOglAUhs+5k9lJFpsJ5QWMJoNGbEY0mEy+gr6GNo0a3SiQCegMRILzGdw4hl+Cd27KxPuXb2zA/91z2YXoGRERkX4fvN3A2QxUiv4dFM3n8jZRBLbbVfd+ubJuF4xjiCyXkksueb1uSKCIZYGLBTEx8ekEoV7PkICeVgs8HiGyXoO2bUigCDM4HoPnM7bI8wwJ6Gk0sEXbLSay30Oo2TQkoGcwgFCSQMhxDAvoETEscDiQkJC4LjMz8+XyZ4HrFYWjEQqHQ1asWGWZfmdFAsVINxuw00HhbvfpydpvxWkKTqdYaRCUfUPJCdzv4Gr1uqfli0tOIAzByUT/iCrL6+84y3Bw+D6ui5Ou+jwA8FnIO++FACgAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDEtMDhUMTY6NDI6NTMrMDg6MDCKP7wnAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAw+2IEmwAAAEl0RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fZ2Y3MDBzN2IzZncvYm9mYW5nLnN2Z8fICi0AAAAASUVORK5CYII='; | |
| 112 | + var pauseBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAHVJREFUSMftkCESwCAMBEOnCtdXVMKHeC7oInkEeQJXkRoEZWraipxZc8lsQqQZBACAlIS1oqGhhTCdu3oyxyyMcdRf79c5J7SWDBky+z4173rbJvR+VF/e/qwKqIAKqMBDgZyFzAQCoZTpxq7HLDyOrw/9b07l3z4dDnI2IAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMIo/vCcAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDEtMDhUMTY6NDI6NTMrMDg6MDD7YgSbAAAASnRFWHRzdmc6YmFzZS11cmkAZmlsZTovLy9ob21lL2FkbWluL2ljb24tZm9udC90bXAvaWNvbl9nZjcwMHM3YjNmdy96YW50aW5nLnN2ZxqNZJkAAAAASUVORK5CYII='; | |
| 113 | + var screenshotBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAaxJREFUSMfNlLFOAkEQhmevAZMjR6OGRBJKsFBzdkYNpYSaWkopIOFRCBWh1ieA+ALGRgutjK0HzV2H5SX7W/zsmY3cnTEhcZovOzcz9+/s7Ir8d4OGht7fBwAgjvEri2OTl1ffSf0xAMBxRIkS1e3Se3+vcszEMe/6OqmT/aN2m1wsNu/o5YVsNHI7BgA4PCRfXzfXCwKy1RLbcXZG9nrkzc12jvT8nPU/PtatOThgAx8fuS4WyZ0de2e+T87n5OcnuVqRsxl5cpImQDnKUc7DA1fVqpimZCu+vCSjiNH9PlmpJNTQ0INBErfeafZRAakC6FWKfH9nwU7H/l6rGdqCOx3y7c3U+aOARsMMp+1vNskwTLjulB23XJL1epqA9OshIiKeJxAIoug7UyA4OuLi6Ynr52deu+NjOy4MSc9Ln8rMDpTLybBpaOjdXbJUIqdTm8a/t2fn/RSQewR24HicTLmGhnbdzcPquvYtGY3+PIR24UKBUXd35v6Sk4lN47+9NXm/FBAEedfGTjw9JYdDm76fm6+hoS8ujGAxT6L9Im7bTKeurvIEb92+AES1b6x283XSAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAwij+8JwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMPtiBJsAAABJdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWRtaW4vaWNvbi1mb250L3RtcC9pY29uX2dmNzAwczdiM2Z3L2NhbWVyYS5zdmeyubWEAAAAAElFTkSuQmCC'; | |
| 114 | + var fullscreenBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAALZJREFUSMftVbsORUAQVSj8DomChvh3lU5CoSVCQq2RObeYu8XG3deVoHCak81kds7Oaz3vxRcAAMwztOg6vX9d6/3XFQQC+b7iAoFhYE7Tvx9EIFAcy/ftO3MQGAQkCfM4MmeZWyajiLnvmYuCeduMAuSzvRBVYNluFHCssSgFp7Sq9ALKkjnPf9ubRtkDL27HNT3QtsY9cAjsNAVheHIKBOwD2wpxFHDbJpwmaHH2L1iWx+2BDy8RbXXtqbRBAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAwij+8JwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMPtiBJsAAABTdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWRtaW4vaWNvbi1mb250L3RtcC9pY29uX2dmNzAwczdiM2Z3L3F1YW5waW5nenVpZGFodWEuc3ZnTBoI7AAAAABJRU5ErkJggg=='; | |
| 115 | + var minScreenBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAYJJREFUSMfdVbGKwkAQnQn+geAfWBixUTsVgp3YGKxSWflVNmIjARULwc5KO40ipNHWRgs/wGLniucKa+Jd5ODuuGle5u3szGRmd5bor4iIiMhuB3Sc+HXXBdp2/Lpta7v4dccRJUrUdhtNQIkSVa3C8HwG1uumg34f2OnEB+h0tF1Sv5b+YIsttpZLEhKSdhvscPi8IXFF74GJiYnHY7Cex8zMvFgkbInjmJnv98kqoO30vmhLtaRMB60WtEbDNDudgMUiKiQSzfjOMzFxoQAyCPSfw7/nQZ/PUYnpNGV6OR6BmYzJbzYIoBQCzGaRBDQvJCTdLnTLolg5HN5t6f8V1h/oUT4PrVKJWBotmEzQw+vV3J9Ow851P2/BaoX9Yfh0BrJZYKlk8uUyHOpDeLuBHwzMBJtN2PV6IPUhXK9Nf5cLMAxfluanrmGkRBggtRo03wfq66P/6CsJAnOg+f6rgfZI4BGYiYlHIx048eR6krcnq34kkj1GuVz8+jceo9+SD5A8yGh8CTq7AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAwij+8JwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMPtiBJsAAABNdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWRtaW4vaWNvbi1mb250L3RtcC9pY29uX2dmNzAwczdiM2Z3L3p1aXhpYW9odWEuc3ZnoCFr0AAAAABJRU5ErkJggg=='; | |
| 116 | + var quietBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAR9JREFUSMfVlD0LglAYhe9VkwgNihpsjbYQf4JTS7+iuaGxpcGfJjS0NFRLk2NDi6MogafhJGRIX9yEzvJwrx/nvPd9VYh/F3LkyBuN2g3J1QoAgCQhPe/Hxq5Lo+0WlfJ9dYYAgGaTDAIyy/BUnwcwWJlhcLnZkN2ugIBAuy2kkEL2ep8F73S4kjfFcfn6cMj9KLodrWVBiXyf75tMyOOR+4MBOZ8XLXzorboA5UpnM/J0Ivd7+vX7xX2asqGpVKtFXi5sqWmypXefrfIWAACmU/JwKCoun8hu9zA0uk6u13wgirg+n7+bAcsibbt6SB3n9TQXPxwAwHJJpum7M6BcDDQa0SgMaw9QPkJNIxcLMo4ZcDz+eYDqQFLWbqxKV57EtW1WtMbmAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAwij+8JwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMPtiBJsAAABKdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWRtaW4vaWNvbi1mb250L3RtcC9pY29uX2dmNzAwczdiM2Z3L2ppbmd5aW4uc3ZnIlMYaQAAAABJRU5ErkJggg=='; | |
| 117 | + var playAudioBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAU5JREFUSMftkzGKwlAURf9PULBQwULSCKK1bZAgNuoaFFyAC3AdZg0uQCwshWzAShEEO7Gy0soUCu9Occ3An5nMGCfdzGsO7+Xy3/03iVL/lbAAACiVIBCI77O37Vi9QCDZbEqLm03ycEBUAoHk818v7nYpul5Jz4tf8HBKYa1mcjwmbzd8rG8NFIsU7ffk8UjmcjE3XK+RtB4G2PT75GbDeblMttumfjSKMRCGLxsQCKTReE9KIJDJxDw/SmKxiOZWWh+ntrSlre2WXRAorbTSrZapip7X66kbMKtQUFBQCENznsmQ93vqBhh5r8fO85jAcsnIrcce1yV3uxgD8zl5uZgU+dGBVlrp6GbTKRPwffaDAek45Gz2/M0AAJ0OeTol+w0rFYrOZ3K1MhNJEjEAwHF4cBA8Z8B1zcXV6msv+JMR2yaHQ1LrXx/8Z+sNRxsWcwZeb6UAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDEtMDhUMTY6NDI6NTMrMDg6MDCKP7wnAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAw+2IEmwAAAEt0RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fZ2Y3MDBzN2IzZncvc2hlbmd5aW4uc3ZnFog1MQAAAABJRU5ErkJggg=='; | |
| 118 | + var recordBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAPRJREFUSMflVDEOwjAQO0e8gr2sZYVunREbD6ISfAgmkBjpC/hBEQ+AtTWD6QAI0gBlqRfLp+TiXC5n1nXgMUCS5HBoNBqj6IOMMFwuEpsNAABl6d3HihWrOJaBsuRPkGW+c929HAxuYefb6L+R0ZgkMrJYiItCnCT1sl5Y1jwXj0bNniJNJWqujfX7LyrwJh8AYDxWgulU0dPp20IFlxoODm61kpE4VnS9/puBXyPYgH7LbKY3PhwUnUw+NdC4CdW9+71UgyZspwIBB9No3O0klktxUahyx+Pz+lYG0Xzu84lXRqTqwRQAGAzns8R223gUdxZXGcAK5Hp0ClIAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDEtMDhUMTY6NDI6NTMrMDg6MDCKP7wnAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAw+2IEmwAAAE50RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fZ2Y3MDBzN2IzZncvbHV6aGlzaGlwaW4uc3Zn5Zd7GQAAAABJRU5ErkJggg=='; | |
| 119 | + var recordingBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAahJREFUSMdjYBjpgBFd4NZK+f+soQYG//T+yzFuUFUl2cApjEWM/758UZvysPDn3127GBkZGBgY/v4l6ICb9xTWsRbp6/9f9W8N44Jz5xgCGI4wfGFiIttrR/5n/3/U3KyR8rj8t0RdHS5lcAv+//yXzzhZTY1ii2FAmsGZocna+maD3GnWY62tNzbJBbDOffLkxie5eJYwa2uYMhaigzb2/zyGguPH/y9mTGKYYGlJUIMiYxDjHCen/4oMDAxznJzg4k8Z/jP+l5LCCAFCQP30Y5dfXVZWDI7/zzIs8PNjNGJ4/7/r+XNKA4rkoNZ4/lj0V9TmzUxJv0J+F+jrM3YyvPq/acsWujmA2oBkB9y4LifLxhoa+teAzYFtwtWr/8sZxBj9fHxo7oCbprJ72MqOHWNgZGBkYFy1isGGoZahTFSU0hAgOhcQnfph4P7/df9T9u1jPMn4nyHmxIn/bAzLGe7GxTHsZyj+f+zpUwYGBmmG6bQsiMr+L/v/rqlJY9Njm9889fW4lGEUxXCHwAomUgH3vxBG8c+f1WWf9P98sns3oaJ4FAAAbtWqHTT84QYAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDEtMDhUMTY6MzU6MjMrMDg6MDBLHbvEAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAxLTA4VDE2OjM1OjIzKzA4OjAwOkADeAAAAE50RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fcTM1YTFhNHBtY2MvbHV6aGlzaGlwaW4uc3Zn6xlv1QAAAABJRU5ErkJggg=='; | |
| 120 | + var gifBase64 = 'data:image/gif;base64,R0lGODlhgACAAKIAAP///93d3bu7u5mZmQAA/wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAEACwCAAIAfAB8AAAD/0i63P4wygYqmDjrzbtflvWNZGliYXiubKuloivPLlzReD7al+7/Eh5wSFQIi8hHYBkwHUmD6CD5YTJLz49USuVYraRsZ7vtar7XnQ1Kjpoz6LRHvGlz35O4nEPP2O94EnpNc2sef1OBGIOFMId/inB6jSmPdpGScR19EoiYmZobnBCIiZ95k6KGGp6ni4wvqxilrqBfqo6skLW2YBmjDa28r6Eosp27w8Rov8ekycqoqUHODrTRvXsQwArC2NLF29UM19/LtxO5yJd4Au4CK7DUNxPebG4e7+8n8iv2WmQ66BtoYpo/dvfacBjIkITBE9DGlMvAsOIIZjIUAixliv9ixYZVtLUos5GjwI8gzc3iCGghypQqrbFsme8lwZgLZtIcYfNmTJ34WPTUZw5oRxdD9w0z6iOpO15MgTh1BTTJUKos39jE+o/KS64IFVmsFfYT0aU7capdy7at27dw48qdS7eu3bt480I02vUbX2F/JxYNDImw4GiGE/P9qbhxVpWOI/eFKtlNZbWXuzlmG1mv58+gQ4seTbq06dOoU6vGQZJy0FNlMcV+czhQ7SQmYd8eMhPs5BxVdfcGEtV3buDBXQ+fURxx8oM6MT9P+Fh6dOrH2zavc13u9JXVJb520Vp8dvC76wXMuN5Sepm/1WtkEZHDefnzR9Qvsd9+/wi8+en3X0ntYVcSdAE+UN4zs7ln24CaLagghIxBaGF8kFGoIYV+Ybghh841GIyI5ICIFoklJsigihmimJOLEbLYIYwxSgigiZ+8l2KB+Ml4oo/w8dijjcrouCORKwIpnJIjMnkkksalNeR4fuBIm5UEYImhIlsGCeWNNJphpJdSTlkml1jWeOY6TnaRpppUctcmFW9mGSaZceYopH9zkjnjUe59iR5pdapWaGqHopboaYua1qije67GJ6CuJAAAIfkEBQUABAAsCgACAFcAMAAAA/9Iutz+ML5Ag7w46z0r5WAoSp43nihXVmnrdusrv+s332dt4Tyo9yOBUJD6oQBIQGs4RBlHySSKyczVTtHoidocPUNZaZAr9F5FYbGI3PWdQWn1mi36buLKFJvojsHjLnshdhl4L4IqbxqGh4gahBJ4eY1kiX6LgDN7fBmQEJI4jhieD4yhdJ2KkZk8oiSqEaatqBekDLKztBG2CqBACq4wJRi4PZu1sA2+v8C6EJexrBAD1AOBzsLE0g/V1UvYR9sN3eR6lTLi4+TlY1wz6Qzr8u1t6FkY8vNzZTxaGfn6mAkEGFDgL4LrDDJDyE4hEIbdHB6ESE1iD4oVLfLAqPETIsOODwmCDJlv5MSGJklaS6khAQAh+QQFBQAEACwfAAIAVwAwAAAD/0i63P5LSAGrvTjrNuf+YKh1nWieIumhbFupkivPBEzR+GnnfLj3ooFwwPqdAshAazhEGUXJJIrJ1MGOUamJ2jQ9QVltkCv0XqFh5IncBX01afGYnDqD40u2z76JK/N0bnxweC5sRB9vF34zh4gjg4uMjXobihWTlJUZlw9+fzSHlpGYhTminKSepqebF50NmTyor6qxrLO0L7YLn0ALuhCwCrJAjrUqkrjGrsIkGMW/BMEPJcphLgDaABjUKNEh29vdgTLLIOLpF80s5xrp8ORVONgi8PcZ8zlRJvf40tL8/QPYQ+BAgjgMxkPIQ6E6hgkdjoNIQ+JEijMsasNY0RQix4gKP+YIKXKkwJIFF6JMudFEAgAh+QQFBQAEACw8AAIAQgBCAAAD/kg0PPowykmrna3dzXvNmSeOFqiRaGoyaTuujitv8Gx/661HtSv8gt2jlwIChYtc0XjcEUnMpu4pikpv1I71astytkGh9wJGJk3QrXlcKa+VWjeSPZHP4Rtw+I2OW81DeBZ2fCB+UYCBfWRqiQp0CnqOj4J1jZOQkpOUIYx/m4oxg5cuAaYBO4Qop6c6pKusrDevIrG2rkwptrupXB67vKAbwMHCFcTFxhLIt8oUzLHOE9Cy0hHUrdbX2KjaENzey9Dh08jkz8Tnx83q66bt8PHy8/T19vf4+fr6AP3+/wADAjQmsKDBf6AOKjS4aaHDgZMeSgTQcKLDhBYPEswoA1BBAgAh+QQFBQAEACxOAAoAMABXAAAD7Ei6vPOjyUkrhdDqfXHm4OZ9YSmNpKmiqVqykbuysgvX5o2HcLxzup8oKLQQix0UcqhcVo5ORi+aHFEn02sDeuWqBGCBkbYLh5/NmnldxajX7LbPBK+PH7K6narfO/t+SIBwfINmUYaHf4lghYyOhlqJWgqDlAuAlwyBmpVnnaChoqOkpaanqKmqKgGtrq+wsbA1srW2ry63urasu764Jr/CAb3Du7nGt7TJsqvOz9DR0tPU1TIA2ACl2dyi3N/aneDf4uPklObj6OngWuzt7u/d8fLY9PXr9eFX+vv8+PnYlUsXiqC3c6PmUUgAACH5BAUFAAQALE4AHwAwAFcAAAPpSLrc/m7IAau9bU7MO9GgJ0ZgOI5leoqpumKt+1axPJO1dtO5vuM9yi8TlAyBvSMxqES2mo8cFFKb8kzWqzDL7Xq/4LB4TC6bz1yBes1uu9uzt3zOXtHv8xN+Dx/x/wJ6gHt2g3Rxhm9oi4yNjo+QkZKTCgGWAWaXmmOanZhgnp2goaJdpKGmp55cqqusrZuvsJays6mzn1m4uRAAvgAvuBW/v8GwvcTFxqfIycA3zA/OytCl0tPPO7HD2GLYvt7dYd/ZX99j5+Pi6tPh6+bvXuTuzujxXens9fr7YPn+7egRI9PPHrgpCQAAIfkEBQUABAAsPAA8AEIAQgAAA/lIutz+UI1Jq7026h2x/xUncmD5jehjrlnqSmz8vrE8u7V5z/m5/8CgcEgsGo/IpHLJbDqf0Kh0ShBYBdTXdZsdbb/Yrgb8FUfIYLMDTVYz2G13FV6Wz+lX+x0fdvPzdn9WeoJGAYcBN39EiIiKeEONjTt0kZKHQGyWl4mZdREAoQAcnJhBXBqioqSlT6qqG6WmTK+rsa1NtaGsuEu6o7yXubojsrTEIsa+yMm9SL8osp3PzM2cStDRykfZ2tfUtS/bRd3ewtzV5pLo4eLjQuUp70Hx8t9E9eqO5Oku5/ztdkxi90qPg3x2EMpR6IahGocPCxp8AGtigwQAIfkEBQUABAAsHwBOAFcAMAAAA/9Iutz+MMo36pg4682J/V0ojs1nXmSqSqe5vrDXunEdzq2ta3i+/5DeCUh0CGnF5BGULC4tTeUTFQVONYAs4CfoCkZPjFar83rBx8l4XDObSUL1Ott2d1U4yZwcs5/xSBB7dBMBhgEYfncrTBGDW4WHhomKUY+QEZKSE4qLRY8YmoeUfkmXoaKInJ2fgxmpqqulQKCvqRqsP7WooriVO7u8mhu5NacasMTFMMHCm8qzzM2RvdDRK9PUwxzLKdnaz9y/Kt8SyR3dIuXmtyHpHMcd5+jvWK4i8/TXHff47SLjQvQLkU+fG29rUhQ06IkEG4X/Rryp4mwUxSgLL/7IqFETB8eONT6ChCFy5ItqJomES6kgAQAh+QQFBQAEACwKAE4AVwAwAAAD/0i63A4QuEmrvTi3yLX/4MeNUmieITmibEuppCu3sDrfYG3jPKbHveDktxIaF8TOcZmMLI9NyBPanFKJp4A2IBx4B5lkdqvtfb8+HYpMxp3Pl1qLvXW/vWkli16/3dFxTi58ZRcChwIYf3hWBIRchoiHiotWj5AVkpIXi4xLjxiaiJR/T5ehoomcnZ+EGamqq6VGoK+pGqxCtaiiuJVBu7yaHrk4pxqwxMUzwcKbyrPMzZG90NGDrh/JH8t72dq3IN1jfCHb3L/e5ebh4ukmxyDn6O8g08jt7tf26ybz+m/W9GNXzUQ9fm1Q/APoSWAhhfkMAmpEbRhFKwsvCsmosRIHx444PoKcIXKkjIImjTzjkQAAIfkEBQUABAAsAgA8AEIAQgAAA/VIBNz+8KlJq72Yxs1d/uDVjVxogmQqnaylvkArT7A63/V47/m2/8CgcEgsGo/IpHLJbDqf0Kh0Sj0FroGqDMvVmrjgrDcTBo8v5fCZki6vCW33Oq4+0832O/at3+f7fICBdzsChgJGeoWHhkV0P4yMRG1BkYeOeECWl5hXQ5uNIAOjA1KgiKKko1CnqBmqqk+nIbCkTq20taVNs7m1vKAnurtLvb6wTMbHsUq4wrrFwSzDzcrLtknW16tI2tvERt6pv0fi48jh5h/U6Zs77EXSN/BE8jP09ZFA+PmhP/xvJgAMSGBgQINvEK5ReIZhQ3QEMTBLAAAh+QQFBQAEACwCAB8AMABXAAAD50i6DA4syklre87qTbHn4OaNYSmNqKmiqVqyrcvBsazRpH3jmC7yD98OCBF2iEXjBKmsAJsWHDQKmw571l8my+16v+CweEwum8+hgHrNbrvbtrd8znbR73MVfg838f8BeoB7doN0cYZvaIuMjY6PkJGSk2gClgJml5pjmp2YYJ6dX6GeXaShWaeoVqqlU62ir7CXqbOWrLafsrNctjIDwAMWvC7BwRWtNsbGFKc+y8fNsTrQ0dK3QtXAYtrCYd3eYN3c49/a5NVj5eLn5u3s6e7x8NDo9fbL+Mzy9/T5+tvUzdN3Zp+GBAAh+QQJBQAEACwCAAIAfAB8AAAD/0i63P4wykmrvTjrzbv/YCiOZGmeaKqubOu+cCzPdArcQK2TOL7/nl4PSMwIfcUk5YhUOh3M5nNKiOaoWCuWqt1Ou16l9RpOgsvEMdocXbOZ7nQ7DjzTaeq7zq6P5fszfIASAYUBIYKDDoaGIImKC4ySH3OQEJKYHZWWi5iZG0ecEZ6eHEOio6SfqCaqpaytrpOwJLKztCO2jLi1uoW8Ir6/wCHCxMG2x7muysukzb230M6H09bX2Nna29zd3t/g4cAC5OXm5+jn3Ons7eba7vHt2fL16tj2+QL0+vXw/e7WAUwnrqDBgwgTKlzIsKHDh2gGSBwAccHEixAvaqTYcFCjRoYeNyoM6REhyZIHT4o0qPIjy5YTTcKUmHImx5cwE85cmJPnSYckK66sSAAj0aNIkypdyrSp06dQo0qdSrWq1atYs2rdyrWr169gwxZJAAA7'; | |
| 121 | + var playBigBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwEAYAAAAHkiXEAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAByBJREFUeNrlXFlIVV0U3vsaaINmZoX0YAR6y8oGMkKLoMESSjBoUJEoIogoIggigoryIQoKGqi3Roh6TKGBIkNEe6hMgzTNKLPSUlMrNdvrf/juurlP5zpc7znb+r+X755pn7W+Pe+9zpVimIEUKVKJiUIKKWRqKs5OmwZOTBQkSFBUFK5HR+tPt7WBOzpwX3U1jquqwGVleK6iQkoppSQy7a8xEBERLVwIPnsWXF9PrqCxEXzxInjpUrDH47YO0h2hw8JwtG4deN8+8OzZA0vl7Vt/iZZCCtnUhPPt7fp9o0fjvpgYHHu9uD8+Hsdsh52hggTV1uLg2DHwpUvSIz3S093ttE4hB5qSxYuRAc+f910im5vBFy6As7LALORQ7RgzBullZIBPngQ3NPRt1+vXeH7NGtN69u8oERFFRIDPnQMrZe8YZ0huLhwMDzdjb1gYC4zj4uKAeaFIkbpxAwfWvse48FOngp89s7eeS1p2Nlg63vQF7Y8iRWrlSthZXR2wZhAR0dy55gwlIqI5c8AfPtgbeuUKHIqKMi3soP3z1UzwiRP2NbqtDbxsmXuGacK3tOgG/fwJ3rbNtIDO+J2ZiQzp6ND97uzE+RUrHDaAmxprif/+HQasXm1aKKcBPxcsADc1/VEjFClS8+eH7oXcuSpSpJ480V/Y0wPOyjItjNtgofWmiPHuHa7Hxg79RUT0e1Rjxb/X1ASnDw9vf/3S9bl1K/iEFSlSixbZdz7Xr5t2fLgBuuTn2xfUjRsHmVBYGNg6gWpo+FtHNU4DuowYAZ3Ky+11GzOm/4SIiGjDBvuczM52zAHua4iI6OpVcGEheO1a8PCdP/j9CNRyKFKk9u4doBDWCRXXBOcE0GekgVBUhPuSk00LPTAdCwp0+3n0GBER4AFenbQiJ8cdg7dvpwGB5xunT4PHjTMtuL0/qan29q9fH+AB62jnyxe31moGlwFWNDbCzq1bcez+snLffr14odtrMzrCBet6/Pnz7hoabAZY8fgxT5iGRwbs36/b19kJHjnS49+BEkIIMXmy/vjt26YdCA4pKdgHKC2Fo5cvh2xiFBTu3NGPw8Ox/5CW5tG3/hi8VffokRmDQwUeNOTlwc/KSmRIbq67djx9Cm5p+W2akEKmpfnaSt5zZdTXY8+0udmQcg5h0iQwD3MfPgRPn+7UG6GjUjiqrNSver0eVIWEBP85EiSIN7H/dSxZAuY1roMHHRt02OqamOhrgnoN46SQQn76ZFoad8Hj8kOH4D/PZJOSQvYKW11jYnxNkHWK3NFhWhKz8HrB9+7xaCU06fYKIiBBgiIjfRlgHTf/j+NlNMTFgceOHXJSJEgQ9wXCVyOk9AlvLfEDWDT6X+DAAXSiHz8OOSkppJCRkfrJ9vYR+NHaql8wNV42jVevUFJ37kQ8kHX8PlRMmOD/SYIEtbZ69IAkvsATs38dP36ADx8GJyc7IzyD+xbhqxE1Nb4a8PKlfiE+HsOxyEgYZI1A+9tRUADetQtNTF2dU29CJ84Twhkz9KtVVb4+oKxMvxAWxjM101KFBvX1qNmbNkHwNWucFl4HT/QmTvSfIkGCSks9HC2MsxxzyTekp5uWLjh0dYHz88FeL2ry5ctm7LHq2NMD7rXUg6rC0cKM9+/BfQS1hghDXg1VpEjdvasvLpqHf3VWs/P+/QA3Lltm75jz8T7BZQAvn9tscJgWXpEiNWuWvd2bNwcQwONbnq6p0R8oLnYnA7Zs6Vvw7m7Yd/z4gDe5DQH2Xrum29/SwoObfh7cts1egFWrnDU4Lg785g2Ytx4LC2H4zJmmhe3XD5+dsJsD1xhHjgwwgfBwPFBXpydQXe3uFqXzfU9o7ZUSXFRkX/IHMcENGKXgixY27fBwA8TZudO+5dixY4gJ37xpyQVfvEtmpmnHTQMFMiUFevBeL6OkZMg1GQlER4P5wwTGt29g65bmvw/4HShanD+5mjIlxC+cNw/cKxqYw7RDHZY9TOEXXpEiVVurC8+jtJUrnTNAkSK1fDle2NWlG9DeDs7IMC2UM35zU2Mt8Urhel6eywalp+vCMzhM++hRDlo1LeCg/dNGNdy5Wtt4LvEuCv+HodqHCu/e2Y8Cyss5aNW0sAPzh8fx1uEkgyMGHWxqgjM8NhYGWoNSraMnvm6+89aXDHjmap1AMUpKcD9/+D2MAYNzcsD9fRDNsZMcwsedfehiPJFeUhJ4925wWVnfdvFHiDt2gEM/MXT+rwp47UMKKeT27Ti7Zw+YA6UCgbdKKyr8cTVSSCEbG3Ge/5yDwWtD48fjfv6rAl7C6LUeb4uvX8FnzuD5U6ewjP35s9M6uQaUJP4Qgz8E4SbJ2sk5BV5jevAAvHmzqS9/hs0XJxBi1CgOWtVjVnlHKSEB16Oj/wgoE0L8LsFcM169AldV8Q4UjouKULKtNch9/AdsEf6XQYgIsAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMS0xMlQxMTo1NjowNSswODowMGcMj/QAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDEtMTJUMTE6NTY6MDUrMDg6MDAWUTdIAAAASXRFWHRzdmc6YmFzZS11cmkAZmlsZTovLy9ob21lL2FkbWluL2ljb24tZm9udC90bXAvaWNvbl9wZHMzeWYxNGczYi9ib2Zhbmcuc3Zn11us5wAAAABJRU5ErkJggg=='; | |
| 122 | + | |
| 123 | + function _setStyle(dom, cssObj) { | |
| 124 | + Object.keys(cssObj).forEach(function (key) { | |
| 125 | + dom.style[key] = cssObj[key]; | |
| 126 | + }) | |
| 127 | + } | |
| 128 | + | |
| 129 | + var doms = {}; | |
| 130 | + | |
| 131 | + var fragment = document.createDocumentFragment(); | |
| 132 | + var btnWrap = document.createElement('div'); | |
| 133 | + var control1 = document.createElement('div'); | |
| 134 | + var control2 = document.createElement('div'); | |
| 135 | + var textDom = document.createElement('div'); | |
| 136 | + var speedDom = document.createElement('div'); | |
| 137 | + var playDom = document.createElement('div'); | |
| 138 | + var playBigDom = document.createElement('div'); | |
| 139 | + var pauseDom = document.createElement('div'); | |
| 140 | + var screenshotsDom = document.createElement('div'); | |
| 141 | + var fullscreenDom = document.createElement('div'); | |
| 142 | + var minScreenDom = document.createElement('div'); | |
| 143 | + var loadingDom = document.createElement('div'); | |
| 144 | + var loadingTextDom = document.createElement('div'); | |
| 145 | + var quietAudioDom = document.createElement('div'); | |
| 146 | + var playAudioDom = document.createElement('div'); | |
| 147 | + var recordDom = document.createElement('div'); | |
| 148 | + var recordingDom = document.createElement('div'); | |
| 149 | + var bgDom = document.createElement('div'); | |
| 150 | + | |
| 151 | + loadingTextDom.innerText = this._opt.loadingText || ''; | |
| 152 | + textDom.innerText = this._opt.text || ''; | |
| 153 | + speedDom.innerText = ''; | |
| 154 | + playDom.title = '播放'; | |
| 155 | + pauseDom.title = '暂停'; | |
| 156 | + screenshotsDom.title = '截屏'; | |
| 157 | + fullscreenDom.title = '全屏'; | |
| 158 | + minScreenDom.title = '退出全屏'; | |
| 159 | + quietAudioDom.title = '静音'; | |
| 160 | + playAudioDom.title = '取消静音'; | |
| 161 | + recordDom.title = '录制'; | |
| 162 | + recordingDom.title = '取消录制'; | |
| 163 | + | |
| 164 | + var wrapStyle = { | |
| 165 | + height: '38px', | |
| 166 | + zIndex: 11, | |
| 167 | + position: 'absolute', | |
| 168 | + left: 0, | |
| 169 | + bottom: 0, | |
| 170 | + width: '100%', | |
| 171 | + background: 'rgba(0,0,0)' | |
| 172 | + }; | |
| 173 | + | |
| 174 | + var bgStyle = { | |
| 175 | + position: 'absolute', | |
| 176 | + width: '100%', | |
| 177 | + height: '100%', | |
| 178 | + }; | |
| 179 | + | |
| 180 | + if (this._opt.background) { | |
| 181 | + bgStyle = Object.assign({}, bgStyle, { | |
| 182 | + backgroundRepeat: "no-repeat", | |
| 183 | + backgroundPosition: "center", | |
| 184 | + backgroundSize: '100%', | |
| 185 | + backgroundImage: "url('" + this._opt.background + "')" | |
| 186 | + }) | |
| 187 | + } | |
| 188 | + | |
| 189 | + // | |
| 190 | + var loadingStyle = { | |
| 191 | + position: 'absolute', | |
| 192 | + width: '100%', | |
| 193 | + height: '100%', | |
| 194 | + textAlign: 'center', | |
| 195 | + color: "#fff", | |
| 196 | + display: 'none', | |
| 197 | + backgroundImage: "url('" + gifBase64 + "')", | |
| 198 | + backgroundRepeat: "no-repeat", | |
| 199 | + backgroundPosition: "center", | |
| 200 | + backgroundSize: "40px 40px", | |
| 201 | + }; | |
| 202 | + | |
| 203 | + var playBigStyle = { | |
| 204 | + position: 'absolute', | |
| 205 | + width: '100%', | |
| 206 | + height: '100%', | |
| 207 | + display: 'none', | |
| 208 | + background: 'rgba(0,0,0,0.4)', | |
| 209 | + backgroundImage: "url('" + playBigBase64 + "')", | |
| 210 | + backgroundRepeat: "no-repeat", | |
| 211 | + backgroundPosition: "center", | |
| 212 | + backgroundSize: "48px 48px", | |
| 213 | + cursor: "pointer" | |
| 214 | + }; | |
| 215 | + | |
| 216 | + var loadingTextStyle = { | |
| 217 | + position: 'absolute', | |
| 218 | + width: "100%", | |
| 219 | + top: '60%', | |
| 220 | + textAlign: 'center', | |
| 221 | + } | |
| 222 | + var controlStyle = { | |
| 223 | + position: 'absolute', | |
| 224 | + top: 0, | |
| 225 | + height: '100%', | |
| 226 | + display: 'flex', | |
| 227 | + alignItems: 'center', | |
| 228 | + }; | |
| 229 | + var styleObj = { | |
| 230 | + display: 'none', | |
| 231 | + position: 'relative', | |
| 232 | + fontSize: '13px', | |
| 233 | + color: '#fff', | |
| 234 | + lineHeight: '20px', | |
| 235 | + marginLeft: '5px', | |
| 236 | + marginRight: '5px', | |
| 237 | + userSelect: 'none' | |
| 238 | + }; | |
| 239 | + var styleObj2 = { | |
| 240 | + display: 'none', | |
| 241 | + position: 'relative', | |
| 242 | + width: '16px', | |
| 243 | + height: '16px', | |
| 244 | + marginLeft: '8px', | |
| 245 | + marginRight: '8px', | |
| 246 | + backgroundRepeat: "no-repeat", | |
| 247 | + backgroundPosition: "center", | |
| 248 | + backgroundSize: '100%', | |
| 249 | + cursor: 'pointer', | |
| 250 | + }; | |
| 251 | + _setStyle(bgDom, bgStyle); | |
| 252 | + _setStyle(btnWrap, wrapStyle); | |
| 253 | + _setStyle(loadingDom, loadingStyle); | |
| 254 | + _setStyle(playBigDom, playBigStyle); | |
| 255 | + _setStyle(loadingTextDom, loadingTextStyle); | |
| 256 | + _setStyle(control1, Object.assign({}, controlStyle, { | |
| 257 | + left: 0 | |
| 258 | + })); | |
| 259 | + _setStyle(control2, Object.assign({}, controlStyle, { | |
| 260 | + right: 0 | |
| 261 | + })); | |
| 262 | + _setStyle(textDom, styleObj); | |
| 263 | + _setStyle(speedDom, styleObj); | |
| 264 | + _setStyle(playDom, Object.assign({}, styleObj2, { | |
| 265 | + backgroundImage: "url('" + playBase64 + "')", | |
| 266 | + })); | |
| 267 | + | |
| 268 | + _setStyle(pauseDom, Object.assign({}, styleObj2, { | |
| 269 | + backgroundImage: "url('" + pauseBase64 + "')" | |
| 270 | + })); | |
| 271 | + | |
| 272 | + _setStyle(screenshotsDom, Object.assign({}, styleObj2, { | |
| 273 | + backgroundImage: "url('" + screenshotBase64 + "')" | |
| 274 | + })); | |
| 275 | + | |
| 276 | + _setStyle(fullscreenDom, Object.assign({}, styleObj2, { | |
| 277 | + backgroundImage: "url('" + fullscreenBase64 + "')" | |
| 278 | + })); | |
| 279 | + | |
| 280 | + _setStyle(minScreenDom, Object.assign({}, styleObj2, { | |
| 281 | + backgroundImage: "url('" + minScreenBase64 + "')" | |
| 282 | + })); | |
| 283 | + | |
| 284 | + _setStyle(quietAudioDom, Object.assign({}, styleObj2, { | |
| 285 | + backgroundImage: "url('" + quietBase64 + "')" | |
| 286 | + })); | |
| 287 | + | |
| 288 | + _setStyle(playAudioDom, Object.assign({}, styleObj2, { | |
| 289 | + backgroundImage: "url('" + playAudioBase64 + "')" | |
| 290 | + })); | |
| 291 | + | |
| 292 | + _setStyle(recordDom, Object.assign({}, styleObj2, { | |
| 293 | + backgroundImage: "url('" + recordBase64 + "')" | |
| 294 | + })); | |
| 295 | + | |
| 296 | + _setStyle(recordingDom, Object.assign({}, styleObj2, { | |
| 297 | + backgroundImage: "url('" + recordingBase64 + "')" | |
| 298 | + })); | |
| 299 | + | |
| 300 | + loadingDom.appendChild(loadingTextDom); | |
| 301 | + if (this._opt.text) { | |
| 302 | + control1.appendChild(textDom); | |
| 303 | + doms.textDom = textDom; | |
| 304 | + } | |
| 305 | + if (this._opt.showBandwidth) { | |
| 306 | + control1.appendChild(speedDom); | |
| 307 | + doms.speedDom = speedDom; | |
| 308 | + } | |
| 309 | + | |
| 310 | + // record | |
| 311 | + //control2.appendChild(recordingDom); | |
| 312 | + //control2.appendChild(recordDom); | |
| 313 | + | |
| 314 | + // screenshots | |
| 315 | + if (this._opt.operateBtns.screenshot) { | |
| 316 | + control2.appendChild(screenshotsDom); | |
| 317 | + doms.screenshotsDom = screenshotsDom; | |
| 318 | + } | |
| 319 | + | |
| 320 | + // play stop | |
| 321 | + if (this._opt.operateBtns.play) { | |
| 322 | + control2.appendChild(playDom); | |
| 323 | + control2.appendChild(pauseDom); | |
| 324 | + doms.playDom = playDom; | |
| 325 | + doms.pauseDom = pauseDom; | |
| 326 | + } | |
| 327 | + | |
| 328 | + // audio | |
| 329 | + if (this._opt.operateBtns.audio) { | |
| 330 | + control2.appendChild(playAudioDom); | |
| 331 | + control2.appendChild(quietAudioDom); | |
| 332 | + doms.playAudioDom = playAudioDom; | |
| 333 | + doms.quietAudioDom = quietAudioDom; | |
| 334 | + } | |
| 335 | + | |
| 336 | + // fullscreen | |
| 337 | + if (this._opt.operateBtns.fullscreen) { | |
| 338 | + control2.appendChild(fullscreenDom); | |
| 339 | + control2.appendChild(minScreenDom); | |
| 340 | + doms.fullscreenDom = fullscreenDom; | |
| 341 | + doms.minScreenDom = minScreenDom; | |
| 342 | + } | |
| 343 | + | |
| 344 | + btnWrap.appendChild(control1); | |
| 345 | + btnWrap.appendChild(control2); | |
| 346 | + | |
| 347 | + fragment.appendChild(bgDom); | |
| 348 | + doms.bgDom = bgDom; | |
| 349 | + fragment.appendChild(loadingDom); | |
| 350 | + doms.loadingDom = loadingDom; | |
| 351 | + if (this._showControl()) { | |
| 352 | + fragment.appendChild(btnWrap); | |
| 353 | + } | |
| 354 | + if (this._opt.operateBtns.play) { | |
| 355 | + fragment.appendChild(playBigDom); | |
| 356 | + doms.playBigDom = playBigDom; | |
| 357 | + } | |
| 358 | + this._container.appendChild(fragment); | |
| 359 | + this._doms = doms; | |
| 360 | + }; | |
| 361 | + | |
| 362 | + Jessibuca.prototype._initWakeLock = function () { | |
| 363 | + this._wakeLock = null; | |
| 364 | + var _this = this; | |
| 365 | + var handleWakeLock = () => { | |
| 366 | + if (this._wakeLock !== null && "visible" === document.visibilityState) { | |
| 367 | + _this._enableWakeLock(); | |
| 368 | + } | |
| 369 | + }; | |
| 370 | + | |
| 371 | + document.addEventListener('visibilitychange', handleWakeLock); | |
| 372 | + document.addEventListener('fullscreenchange', handleWakeLock); | |
| 373 | + }; | |
| 374 | + | |
| 375 | + Jessibuca.prototype._enableWakeLock = function () { | |
| 376 | + if (this._opt.keepScreenOn) { | |
| 377 | + if ("wakeLock" in navigator) { | |
| 378 | + var _this = this; | |
| 379 | + navigator.wakeLock.request("screen").then((lock) => { | |
| 380 | + _this._wakeLock = lock; | |
| 381 | + _this._wakeLock.addEventListener('release', function () { | |
| 382 | + }); | |
| 383 | + }) | |
| 384 | + } | |
| 385 | + } | |
| 386 | + }; | |
| 387 | + | |
| 388 | + | |
| 389 | + Jessibuca.prototype._initGainNode = function () { | |
| 390 | + var gainNode = this._audioContext.createGain(); | |
| 391 | + var _this = this; | |
| 392 | + var source; | |
| 393 | + if (!navigator.mediaDevices.getUserMedia) { | |
| 394 | + console.log('getUserMedia not supported on your browser!'); | |
| 395 | + return; | |
| 396 | + } | |
| 397 | + | |
| 398 | + navigator.mediaDevices.getUserMedia( | |
| 399 | + // constraints - only audio needed for this app | |
| 400 | + { | |
| 401 | + audio: true | |
| 402 | + }, | |
| 403 | + | |
| 404 | + // Success callback | |
| 405 | + function (stream) { | |
| 406 | + source = _this._audioContext.createMediaStreamSource(stream); | |
| 407 | + source.connect(gainNode); | |
| 408 | + gainNode.connect(_this._audioContext.destination); | |
| 409 | + _this._gainNode = gainNode; | |
| 410 | + }, | |
| 411 | + | |
| 412 | + // Error callback | |
| 413 | + function (err) { | |
| 414 | + console.log('The following gUM error occurred: ' + err); | |
| 415 | + } | |
| 416 | + ); | |
| 417 | + }; | |
| 418 | + | |
| 419 | + Jessibuca.prototype._showControl = function () { | |
| 420 | + var result = false; | |
| 421 | + | |
| 422 | + var hasBtnShow = false; | |
| 423 | + Object.keys(this._opt.operateBtns).forEach((key) => { | |
| 424 | + if (this._opt.operateBtns[key]) { | |
| 425 | + hasBtnShow = true; | |
| 426 | + } | |
| 427 | + }); | |
| 428 | + | |
| 429 | + if (this._opt.showBandwidth || this._opt.text || hasBtnShow) { | |
| 430 | + result = true; | |
| 431 | + } | |
| 432 | + | |
| 433 | + return result; | |
| 434 | + }; | |
| 435 | + | |
| 436 | + Jessibuca.prototype._onMessage = function () { | |
| 437 | + var _this = this; | |
| 438 | + this._decoderWorker.onmessage = function (event) { | |
| 439 | + var msg = event.data; | |
| 440 | + switch (msg.cmd) { | |
| 441 | + case "init": | |
| 442 | + _this._opt.isDebug && console.log("decoder worker init") | |
| 443 | + _this.setBufferTime(_this._opt.videoBuffer); | |
| 444 | + if (!_this._hasLoaded) { | |
| 445 | + _this._opt.isDebug && console.log("has loaded"); | |
| 446 | + _this._hasLoaded = true; | |
| 447 | + _this.onLoad(); | |
| 448 | + _this._trigger('load'); | |
| 449 | + } | |
| 450 | + break | |
| 451 | + case "initSize": | |
| 452 | + _this._canvasElement.width = msg.w; | |
| 453 | + _this._canvasElement.height = msg.h; | |
| 454 | + _this.onInitSize(); | |
| 455 | + _this.resize(); | |
| 456 | + _this._trigger('videoInfo', {w: msg.w, h: msg.h}); | |
| 457 | + if (_this.isWebGL()) { | |
| 458 | + | |
| 459 | + } else { | |
| 460 | + _this._initRGB(msg.w, msg.h) | |
| 461 | + } | |
| 462 | + break | |
| 463 | + case "render": | |
| 464 | + if (_this._contextGL) { | |
| 465 | + _this._drawNextOutputPictureGL(msg.output); | |
| 466 | + } else { | |
| 467 | + _this._drawNextOutputPictureRGBA(msg.buffer); | |
| 468 | + } | |
| 469 | + if (_this.loading) { | |
| 470 | + _this.loading = false; | |
| 471 | + _this.playing = true; | |
| 472 | + _this._opt.isDebug && console.log("clear check loading timeout"); | |
| 473 | + _this._clearCheckLoading(); | |
| 474 | + } | |
| 475 | + _this._trigger('timeUpdate', msg.ts); | |
| 476 | + _this.onTimeUpdate(msg.ts); | |
| 477 | + _this._updateStats({bps: msg.bps, ts: msg.ts}); | |
| 478 | + _this._checkHeart(); | |
| 479 | + break | |
| 480 | + case "initAudio": | |
| 481 | + _this._initAudioPlay(msg.frameCount, msg.samplerate, msg.channels) | |
| 482 | + _this._trigger('audioInfo', { | |
| 483 | + numOfChannels: msg.channels, // 声频通道 | |
| 484 | + length: msg.frameCount, // 帧数 | |
| 485 | + sampleRate: msg.samplerate // 采样率 | |
| 486 | + }); | |
| 487 | + break | |
| 488 | + case "playAudio": | |
| 489 | + _this._playAudio(msg.buffer) | |
| 490 | + break | |
| 491 | + case "print": | |
| 492 | + _this.onLog(msg.text) | |
| 493 | + this._trigger('log', msg.text); | |
| 494 | + _this._opt.isDebug && console.log(msg.text); | |
| 495 | + break | |
| 496 | + case "printErr": | |
| 497 | + _this.onLog(msg.text); | |
| 498 | + this._trigger('log', msg.text); | |
| 499 | + _this.onError(msg.text); | |
| 500 | + this._trigger('error', msg.text); | |
| 501 | + _this._opt.isDebug && console.error(msg.text); | |
| 502 | + break; | |
| 503 | + case "initAudioPlanar": | |
| 504 | + _this._initAudioPlanar(msg); | |
| 505 | + _this._trigger('audioInfo', { | |
| 506 | + numOfChannels: msg.channels, // 声频通道 | |
| 507 | + length: undefined, // 帧数 | |
| 508 | + sampleRate: msg.samplerate // 采样率 | |
| 509 | + }); | |
| 510 | + break; | |
| 511 | + default: | |
| 512 | + _this._opt.isDebug && console.log(msg); | |
| 513 | + _this[msg.cmd](msg) | |
| 514 | + } | |
| 515 | + }; | |
| 516 | + }; | |
| 517 | + | |
| 518 | + Jessibuca.prototype._initEventListener = function () { | |
| 519 | + var _this = this; | |
| 520 | + | |
| 521 | + this._doms.playDom && this._doms.playDom.addEventListener('click', function (e) { | |
| 522 | + e.stopPropagation(); | |
| 523 | + _this.play(); | |
| 524 | + }, false); | |
| 525 | + | |
| 526 | + this._doms.playBigDom && this._doms.playBigDom.addEventListener('click', function (e) { | |
| 527 | + e.stopPropagation(); | |
| 528 | + _this.play(); | |
| 529 | + }, false); | |
| 530 | + | |
| 531 | + this._doms.pauseDom && this._doms.pauseDom.addEventListener('click', function (e) { | |
| 532 | + e.stopPropagation(); | |
| 533 | + _this.pause(); | |
| 534 | + }, false); | |
| 535 | + | |
| 536 | + // screenshots | |
| 537 | + this._doms.screenshotsDom && this._doms.screenshotsDom.addEventListener('click', function (e) { | |
| 538 | + e.stopPropagation(); | |
| 539 | + var filename = _this._opt.text + '' + _now(); | |
| 540 | + _this._screenshot(filename); | |
| 541 | + }, false); | |
| 542 | + // | |
| 543 | + this._doms.fullscreenDom && this._doms.fullscreenDom.addEventListener('click', function (e) { | |
| 544 | + e.stopPropagation(); | |
| 545 | + _this.fullscreen = true; | |
| 546 | + }, false); | |
| 547 | + // | |
| 548 | + this._doms.minScreenDom && this._doms.minScreenDom.addEventListener('click', function (e) { | |
| 549 | + e.stopPropagation(); | |
| 550 | + _this.fullscreen = false; | |
| 551 | + }, false); | |
| 552 | + // | |
| 553 | + this._doms.recordDom && this._doms.recordDom.addEventListener('click', function (e) { | |
| 554 | + e.stopPropagation(); | |
| 555 | + _this.recording = true; | |
| 556 | + }, false); | |
| 557 | + // | |
| 558 | + this._doms.recordingDom && this._doms.recordingDom.addEventListener('click', function (e) { | |
| 559 | + e.stopPropagation(); | |
| 560 | + _this.recording = false; | |
| 561 | + }, false); | |
| 562 | + | |
| 563 | + this._doms.quietAudioDom && this._doms.quietAudioDom.addEventListener('click', function (e) { | |
| 564 | + e.stopPropagation(); | |
| 565 | + _this.cancelMute(); | |
| 566 | + }, false); | |
| 567 | + | |
| 568 | + this._doms.playAudioDom && this._doms.playAudioDom.addEventListener('click', function (e) { | |
| 569 | + e.stopPropagation(); | |
| 570 | + _this.mute(); | |
| 571 | + }, false); | |
| 572 | + }; | |
| 573 | + /** | |
| 574 | + * set debug | |
| 575 | + * @param flag | |
| 576 | + */ | |
| 577 | + Jessibuca.prototype.setDebug = function (flag) { | |
| 578 | + this._opt.isDebug = !!flag; | |
| 579 | + }; | |
| 580 | + /** | |
| 581 | + * mute | |
| 582 | + */ | |
| 583 | + Jessibuca.prototype.mute = function () { | |
| 584 | + this._audioEnabled(false); | |
| 585 | + this.quieting = true; | |
| 586 | + }; | |
| 587 | + | |
| 588 | + /** | |
| 589 | + * cancel mute | |
| 590 | + */ | |
| 591 | + Jessibuca.prototype.cancelMute = function () { | |
| 592 | + this._audioEnabled(true); | |
| 593 | + this.quieting = false; | |
| 594 | + }; | |
| 595 | + | |
| 596 | + /** | |
| 597 | + * 设置旋转角度 | |
| 598 | + */ | |
| 599 | + Jessibuca.prototype.setRotate = function (deg) { | |
| 600 | + | |
| 601 | + }; | |
| 602 | + | |
| 603 | + Jessibuca.prototype._initStatus = function () { | |
| 604 | + this._loading = true; | |
| 605 | + this.loading = true; | |
| 606 | + this._recording = false; | |
| 607 | + this.recording = false; | |
| 608 | + this._playing = false; | |
| 609 | + this.playing = false; | |
| 610 | + this._quieting = this._opt.isNotMute ? false : true; | |
| 611 | + this.quieting = this._opt.isNotMute ? false : true; | |
| 612 | + this._fullscreen = false; | |
| 613 | + this.fullscreen = false; | |
| 614 | + } | |
| 615 | + | |
| 616 | + Jessibuca.prototype._initBtns = function () { | |
| 617 | + // show | |
| 618 | + _domToggle(this._doms.pauseDom, true); | |
| 619 | + _domToggle(this._doms.screenshotsDom, true); | |
| 620 | + _domToggle(this._doms.fullscreenDom, true); | |
| 621 | + _domToggle(this._doms.quietAudioDom, true); | |
| 622 | + _domToggle(this._doms.textDom, true); | |
| 623 | + _domToggle(this._doms.speedDom, true); | |
| 624 | + _domToggle(this._doms.recordDom, true); | |
| 625 | + // hide | |
| 626 | + _domToggle(this._doms.loadingDom, false); | |
| 627 | + _domToggle(this._doms.playDom, false); | |
| 628 | + _domToggle(this._doms.playBigDom, false); | |
| 629 | + _domToggle(this._doms.bgDom, false); | |
| 630 | + }; | |
| 631 | + | |
| 632 | + Jessibuca.prototype._hideBtns = function () { | |
| 633 | + var _this = this; | |
| 634 | + Object.keys(this._doms).forEach(function (dom) { | |
| 635 | + if (dom !== 'bgDom') { | |
| 636 | + _domToggle(_this._doms[dom], false); | |
| 637 | + } | |
| 638 | + }) | |
| 639 | + }; | |
| 640 | + | |
| 641 | + function _checkFull() { | |
| 642 | + var isFull = document.fullscreenElement || window.webkitFullscreenElement || document.msFullscreenElement; | |
| 643 | + if (isFull === undefined) isFull = false; | |
| 644 | + return !!isFull; | |
| 645 | + } | |
| 646 | + | |
| 647 | + Jessibuca.prototype._updateStats = function (options) { | |
| 648 | + options = options || {}; | |
| 649 | + | |
| 650 | + if (!this._startBpsTime) { | |
| 651 | + this._startBpsTime = _now(); | |
| 652 | + } | |
| 653 | + var _nowTime = _now(); | |
| 654 | + var timestamp = _nowTime - this._startBpsTime; | |
| 655 | + | |
| 656 | + if (timestamp < 1 * 1000) { | |
| 657 | + this._bps += (options.bps || 0); | |
| 658 | + this._stats.fps += 1; | |
| 659 | + this._stats.vbps += parseInt((options.bps || 0)); | |
| 660 | + return; | |
| 661 | + } | |
| 662 | + this._stats.ts = options.ts; | |
| 663 | + this._doms.speedDom && (this._doms.speedDom.innerText = _bpsSize(this._bps)); | |
| 664 | + this._trigger('bps', this._bps); | |
| 665 | + this._trigger('stats', this._stats); | |
| 666 | + this._trigger('performance', _fpsStatus(this._stats.fps)); | |
| 667 | + this._bps = 0; | |
| 668 | + this._stats.fps = 0; | |
| 669 | + this._stats.vbps = 0; | |
| 670 | + this._startBpsTime = _nowTime; | |
| 671 | + }; | |
| 672 | + | |
| 673 | + | |
| 674 | + Jessibuca.prototype._checkHeart = function () { | |
| 675 | + if (this._checkHeartTimeout) { | |
| 676 | + clearTimeout(this._checkHeartTimeout); | |
| 677 | + this._checkHeartTimeout = null; | |
| 678 | + } | |
| 679 | + var _this = this; | |
| 680 | + this._checkHeartTimeout = setTimeout(function () { | |
| 681 | + _this._opt.isDebug && console.log('check heart timeout'); | |
| 682 | + _this._trigger('timeout'); | |
| 683 | + _this.recording = false; | |
| 684 | + _this.playing = false; | |
| 685 | + _this._close(); | |
| 686 | + }, this._opt.timeout * 1000); | |
| 687 | + }; | |
| 688 | + | |
| 689 | + Jessibuca.prototype._checkLoading = function () { | |
| 690 | + if (this._checkLoadingTimeout) { | |
| 691 | + clearTimeout(this._checkLoadingTimeout); | |
| 692 | + this._checkLoadingTimeout = null; | |
| 693 | + } | |
| 694 | + var _this = this; | |
| 695 | + this._checkLoadingTimeout = setTimeout(function () { | |
| 696 | + _this._opt.isDebug && console.log('check loading timeout'); | |
| 697 | + _this._trigger('timeout'); | |
| 698 | + _this.playing = false; | |
| 699 | + _this._close(); | |
| 700 | + _domToggle(_this._doms.loadingDom, false); | |
| 701 | + }, this._opt.timeout * 1000); | |
| 702 | + }; | |
| 703 | + | |
| 704 | + Jessibuca.prototype._clearCheckLoading = function () { | |
| 705 | + if (this._checkLoadingTimeout) { | |
| 706 | + clearTimeout(this._checkLoadingTimeout); | |
| 707 | + this._checkLoadingTimeout = null; | |
| 708 | + } | |
| 709 | + }; | |
| 710 | + | |
| 711 | + Jessibuca.prototype._initCheckVariable = function () { | |
| 712 | + this._startBpsTime = ''; | |
| 713 | + this._bps = 0; | |
| 714 | + if (this._checkHeartTimeout) { | |
| 715 | + clearTimeout(this._checkHeartTimeout); | |
| 716 | + this._checkHeartTimeout = null; | |
| 717 | + } | |
| 718 | + } | |
| 719 | + // | |
| 720 | + Jessibuca.prototype._initAudioPlanar = function (msg) { | |
| 721 | + var channels = msg.channels | |
| 722 | + var samplerate = msg.samplerate | |
| 723 | + var context = this._audioContext; | |
| 724 | + var isPlaying = false; | |
| 725 | + var audioBuffers = []; | |
| 726 | + if (!context) return false; | |
| 727 | + var _this = this | |
| 728 | + this._playAudio = function (buffer) { | |
| 729 | + var frameCount = buffer[0][0].length | |
| 730 | + var audioBuffer = context.createBuffer(channels, frameCount * buffer.length, samplerate); | |
| 731 | + var copyToCtxBuffer = function (fromBuffer) { | |
| 732 | + for (var channel = 0; channel < channels; channel++) { | |
| 733 | + var nowBuffering = audioBuffer.getChannelData(channel); | |
| 734 | + for (var j = 0; j < buffer.length; j++) { | |
| 735 | + for (var i = 0; i < frameCount; i++) { | |
| 736 | + nowBuffering[i + j * frameCount] = fromBuffer[j][channel][i] | |
| 737 | + } | |
| 738 | + //postMessage({ cmd: "setBufferA", buffer: fromBuffer[j] }, '*', fromBuffer[j].map(x => x.buffer)) | |
| 739 | + } | |
| 740 | + } | |
| 741 | + } | |
| 742 | + var playNextBuffer = function () { | |
| 743 | + isPlaying = false; | |
| 744 | + //console.log("~", audioBuffers.length) | |
| 745 | + if (audioBuffers.length) { | |
| 746 | + playAudio(audioBuffers.shift()); | |
| 747 | + } | |
| 748 | + //if (audioBuffers.length > 1) audioBuffers.shift(); | |
| 749 | + }; | |
| 750 | + var playAudio = function (fromBuffer) { | |
| 751 | + if (!fromBuffer) return | |
| 752 | + if (isPlaying) { | |
| 753 | + audioBuffers.push(fromBuffer); | |
| 754 | + //console.log(audioBuffers.length) | |
| 755 | + return; | |
| 756 | + } | |
| 757 | + isPlaying = true; | |
| 758 | + copyToCtxBuffer(fromBuffer); | |
| 759 | + var source = context.createBufferSource(); | |
| 760 | + source.buffer = audioBuffer; | |
| 761 | + source.connect(context.destination); | |
| 762 | + // source.onended = playNextBuffer; | |
| 763 | + source.start(); | |
| 764 | + }; | |
| 765 | + _this._playAudio = playAudio | |
| 766 | + _this.audioInterval = setInterval(playNextBuffer, audioBuffer.duration * 1000); | |
| 767 | + playAudio(buffer) | |
| 768 | + }; | |
| 769 | + } | |
| 770 | + | |
| 771 | + function _unlock(context) { | |
| 772 | + context.resume(); | |
| 773 | + var source = context.createBufferSource(); | |
| 774 | + source.buffer = context.createBuffer(1, 1, 22050); | |
| 775 | + source.connect(context.destination); | |
| 776 | + if (source.noteOn) | |
| 777 | + source.noteOn(0); | |
| 778 | + else | |
| 779 | + source.start(0); | |
| 780 | + } | |
| 781 | + | |
| 782 | + function _domToggle(dom, toggle) { | |
| 783 | + if (dom) { | |
| 784 | + dom.style.display = toggle ? 'block' : "none"; | |
| 785 | + } | |
| 786 | + } | |
| 787 | + | |
| 788 | + function _dataURLToFile(dataURL) { | |
| 789 | + const arr = dataURL.split(","); | |
| 790 | + const bstr = atob(arr[1]); | |
| 791 | + const type = arr[0].replace("data:", "").replace(";base64", "") | |
| 792 | + let n = bstr.length, u8arr = new Uint8Array(n); | |
| 793 | + while (n--) { | |
| 794 | + u8arr[n] = bstr.charCodeAt(n); | |
| 795 | + } | |
| 796 | + return new File([u8arr], 'file', {type}); | |
| 797 | + } | |
| 798 | + | |
| 799 | + function _downloadImg(content, fileName) { | |
| 800 | + const aLink = document.createElement("a"); | |
| 801 | + aLink.download = fileName; | |
| 802 | + aLink.href = URL.createObjectURL(content); | |
| 803 | + aLink.click(); | |
| 804 | + URL.revokeObjectURL(content); | |
| 805 | + } | |
| 806 | + | |
| 807 | + function _bpsSize(value) { | |
| 808 | + if (null == value || value === '') { | |
| 809 | + return "0 KB/S"; | |
| 810 | + } | |
| 811 | + var srcsize = parseFloat(value); | |
| 812 | + var size = srcsize / 1024; | |
| 813 | + size = size.toFixed(2); | |
| 814 | + return size + 'KB/S'; | |
| 815 | + } | |
| 816 | + | |
| 817 | + function _fpsStatus(fps) { | |
| 818 | + var result = 0; | |
| 819 | + if (fps >= 24) { | |
| 820 | + result = 2; | |
| 821 | + } else if (fps >= 15) { | |
| 822 | + result = 1; | |
| 823 | + } | |
| 824 | + | |
| 825 | + return result; | |
| 826 | + } | |
| 827 | + | |
| 828 | + /** | |
| 829 | + * set audio | |
| 830 | + * @param flag | |
| 831 | + */ | |
| 832 | + Jessibuca.prototype._audioEnabled = function (flag) { | |
| 833 | + if (flag) { | |
| 834 | + _unlock(this._audioContext) | |
| 835 | + this._audioEnabled = function (flag) { | |
| 836 | + if (flag) { | |
| 837 | + // 恢复 | |
| 838 | + this._audioContext.resume(); | |
| 839 | + | |
| 840 | + } else { | |
| 841 | + // 暂停 | |
| 842 | + this._audioContext.suspend(); | |
| 843 | + } | |
| 844 | + } | |
| 845 | + } else { | |
| 846 | + this._audioContext.suspend(); | |
| 847 | + } | |
| 848 | + } | |
| 849 | + | |
| 850 | + Jessibuca.prototype._playAudio = function (data) { | |
| 851 | + var context = this._audioContext; | |
| 852 | + var isPlaying = false; | |
| 853 | + var isDecoding = false; | |
| 854 | + if (!context) return false; | |
| 855 | + var audioBuffers = []; | |
| 856 | + var decodeQueue = [] | |
| 857 | + var _this = this | |
| 858 | + var playNextBuffer = function (e) { | |
| 859 | + if (audioBuffers.length) { | |
| 860 | + playBuffer(audioBuffers.shift()) | |
| 861 | + } | |
| 862 | + }; | |
| 863 | + var playBuffer = function (buffer) { | |
| 864 | + isPlaying = true; | |
| 865 | + var audioBufferSouceNode = context.createBufferSource(); | |
| 866 | + audioBufferSouceNode.buffer = buffer; | |
| 867 | + audioBufferSouceNode.connect(context.destination); | |
| 868 | + // audioBufferSouceNode.onended = playNextBuffer; | |
| 869 | + audioBufferSouceNode.start(); | |
| 870 | + if (!_this.audioInterval) { | |
| 871 | + _this.audioInterval = setInterval(playNextBuffer, buffer.duration * 1000 - 1); | |
| 872 | + } | |
| 873 | + } | |
| 874 | + var decodeAudio = function () { | |
| 875 | + if (decodeQueue.length) { | |
| 876 | + context.decodeAudioData(decodeQueue.shift(), tryPlay, decodeAudio); | |
| 877 | + } else { | |
| 878 | + isDecoding = false | |
| 879 | + } | |
| 880 | + } | |
| 881 | + var tryPlay = function (buffer) { | |
| 882 | + decodeAudio() | |
| 883 | + if (isPlaying) { | |
| 884 | + audioBuffers.push(buffer); | |
| 885 | + } else { | |
| 886 | + playBuffer(buffer) | |
| 887 | + } | |
| 888 | + } | |
| 889 | + var playAudio = function (data) { | |
| 890 | + decodeQueue.push(...data) | |
| 891 | + if (!isDecoding) { | |
| 892 | + isDecoding = true | |
| 893 | + decodeAudio() | |
| 894 | + } | |
| 895 | + } | |
| 896 | + this._playAudio = playAudio | |
| 897 | + playAudio(data) | |
| 898 | + } | |
| 899 | + Jessibuca.prototype._initAudioPlay = function (frameCount, samplerate, channels) { | |
| 900 | + var context = this._audioContext; | |
| 901 | + var isPlaying = false; | |
| 902 | + var audioBuffers = []; | |
| 903 | + if (!context) return false; | |
| 904 | + var _this = this | |
| 905 | + var resampled = samplerate < 22050; | |
| 906 | + if (resampled) { | |
| 907 | + _this._opt.isDebug && console.log("resampled!") | |
| 908 | + } | |
| 909 | + var audioBuffer = resampled ? context.createBuffer(channels, frameCount << 1, samplerate << 1) : context.createBuffer(channels, frameCount, samplerate); | |
| 910 | + var playNextBuffer = function () { | |
| 911 | + isPlaying = false; | |
| 912 | + //console.log("~", audioBuffers.length) | |
| 913 | + if (audioBuffers.length) { | |
| 914 | + playAudio(audioBuffers.shift()); | |
| 915 | + } | |
| 916 | + }; | |
| 917 | + | |
| 918 | + var copyToCtxBuffer = channels > 1 ? function (fromBuffer) { | |
| 919 | + for (var channel = 0; channel < channels; channel++) { | |
| 920 | + var nowBuffering = audioBuffer.getChannelData(channel); | |
| 921 | + if (resampled) { | |
| 922 | + for (var i = 0; i < frameCount; i++) { | |
| 923 | + nowBuffering[i * 2] = nowBuffering[i * 2 + 1] = fromBuffer[i * (channel + 1)] / 32768; | |
| 924 | + } | |
| 925 | + } else | |
| 926 | + for (var i = 0; i < frameCount; i++) { | |
| 927 | + nowBuffering[i] = fromBuffer[i * (channel + 1)] / 32768; | |
| 928 | + } | |
| 929 | + | |
| 930 | + } | |
| 931 | + } : function (fromBuffer) { | |
| 932 | + var nowBuffering = audioBuffer.getChannelData(0); | |
| 933 | + for (var i = 0; i < nowBuffering.length; i++) { | |
| 934 | + nowBuffering[i] = fromBuffer[i] / 32768; | |
| 935 | + } | |
| 936 | + }; | |
| 937 | + var playAudio = function (fromBuffer) { | |
| 938 | + if (isPlaying) { | |
| 939 | + audioBuffers.push(fromBuffer); | |
| 940 | + return; | |
| 941 | + } | |
| 942 | + isPlaying = true; | |
| 943 | + copyToCtxBuffer(fromBuffer); | |
| 944 | + var source = context.createBufferSource(); | |
| 945 | + source.buffer = audioBuffer; | |
| 946 | + source.connect(context.destination); | |
| 947 | + if (!_this.audioInterval) { | |
| 948 | + _this.audioInterval = setInterval(playNextBuffer, audioBuffer.duration * 1000); | |
| 949 | + } | |
| 950 | + source.start(); | |
| 951 | + }; | |
| 952 | + this._playAudio = playAudio; | |
| 953 | + } | |
| 954 | + /** | |
| 955 | + * Returns true if the canvas supports WebGL | |
| 956 | + */ | |
| 957 | + Jessibuca.prototype.isWebGL = function () { | |
| 958 | + return !!this._contextGL; | |
| 959 | + }; | |
| 960 | + /** | |
| 961 | + * set timeout | |
| 962 | + * @param time | |
| 963 | + */ | |
| 964 | + Jessibuca.prototype.setTimeout = function (time) { | |
| 965 | + if (typeof time === 'number') { | |
| 966 | + this._opt.timeout = Number(time); | |
| 967 | + } | |
| 968 | + }; | |
| 969 | + | |
| 970 | + /** | |
| 971 | + * @desc 视频缩放模式, 当视频分辨率比例与canvas显示区域比例不同时,缩放效果不同: | |
| 972 | + 0 视频画面完全填充canvas区域,画面会被拉伸 | |
| 973 | + 1 视频画面做等比缩放后,高或宽对齐canvas区域,画面不被拉伸,但有黑边(默认) | |
| 974 | + 2 视频画面做等比缩放后,完全填充canvas区域,画面不被拉伸,没有黑边,但画面显示不全 | |
| 975 | + * @param type | |
| 976 | + * | |
| 977 | + */ | |
| 978 | + Jessibuca.prototype.setScaleMode = function (type) { | |
| 979 | + if (type === 0) { | |
| 980 | + this._opt.isFullResize = false; | |
| 981 | + this._opt.isResize = false; | |
| 982 | + } else if (type === 1) { | |
| 983 | + this._opt.isFullResize = false; | |
| 984 | + this._opt.isResize = true; | |
| 985 | + } else if (type === 2) { | |
| 986 | + this._opt.isFullResize = true; | |
| 987 | + } | |
| 988 | + this.resize(); | |
| 989 | + }; | |
| 990 | + | |
| 991 | + /** | |
| 992 | + * Create the GL context from the canvas element | |
| 993 | + */ | |
| 994 | + Jessibuca.prototype._initContextGL = function () { | |
| 995 | + var canvas = this._canvasElement; | |
| 996 | + var gl = null; | |
| 997 | + | |
| 998 | + var validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; | |
| 999 | + var nameIndex = 0; | |
| 1000 | + | |
| 1001 | + while (!gl && nameIndex < validContextNames.length) { | |
| 1002 | + var contextName = validContextNames[nameIndex]; | |
| 1003 | + | |
| 1004 | + try { | |
| 1005 | + var contextOptions = {preserveDrawingBuffer: true}; | |
| 1006 | + if (this._opt.contextOptions) { | |
| 1007 | + contextOptions = Object.assign(contextOptions, this._opt.contextOptions); | |
| 1008 | + } | |
| 1009 | + | |
| 1010 | + gl = canvas.getContext(contextName, contextOptions); | |
| 1011 | + } catch (e) { | |
| 1012 | + gl = null; | |
| 1013 | + } | |
| 1014 | + | |
| 1015 | + if (!gl || typeof gl.getParameter !== "function") { | |
| 1016 | + gl = null; | |
| 1017 | + } | |
| 1018 | + | |
| 1019 | + ++nameIndex; | |
| 1020 | + } | |
| 1021 | + ; | |
| 1022 | + | |
| 1023 | + this._contextGL = gl; | |
| 1024 | + }; | |
| 1025 | + | |
| 1026 | + /** | |
| 1027 | + * Initialize GL shader program | |
| 1028 | + */ | |
| 1029 | + Jessibuca.prototype._initProgram = function () { | |
| 1030 | + var gl = this._contextGL; | |
| 1031 | + | |
| 1032 | + var vertexShaderScript = [ | |
| 1033 | + 'attribute vec4 vertexPos;', | |
| 1034 | + 'attribute vec4 texturePos;', | |
| 1035 | + 'varying vec2 textureCoord;', | |
| 1036 | + | |
| 1037 | + 'void main()', | |
| 1038 | + '{', | |
| 1039 | + 'gl_Position = vertexPos;', | |
| 1040 | + 'textureCoord = texturePos.xy;', | |
| 1041 | + '}' | |
| 1042 | + ].join('\n'); | |
| 1043 | + | |
| 1044 | + var fragmentShaderScript = [ | |
| 1045 | + 'precision highp float;', | |
| 1046 | + 'varying highp vec2 textureCoord;', | |
| 1047 | + 'uniform sampler2D ySampler;', | |
| 1048 | + 'uniform sampler2D uSampler;', | |
| 1049 | + 'uniform sampler2D vSampler;', | |
| 1050 | + 'const mat4 YUV2RGB = mat4', | |
| 1051 | + '(', | |
| 1052 | + '1.1643828125, 0, 1.59602734375, -.87078515625,', | |
| 1053 | + '1.1643828125, -.39176171875, -.81296875, .52959375,', | |
| 1054 | + '1.1643828125, 2.017234375, 0, -1.081390625,', | |
| 1055 | + '0, 0, 0, 1', | |
| 1056 | + ');', | |
| 1057 | + | |
| 1058 | + 'void main(void) {', | |
| 1059 | + 'highp float y = texture2D(ySampler, textureCoord).r;', | |
| 1060 | + 'highp float u = texture2D(uSampler, textureCoord).r;', | |
| 1061 | + 'highp float v = texture2D(vSampler, textureCoord).r;', | |
| 1062 | + 'gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;', | |
| 1063 | + '}' | |
| 1064 | + ].join('\n'); | |
| 1065 | + | |
| 1066 | + var vertexShader = gl.createShader(gl.VERTEX_SHADER); | |
| 1067 | + gl.shaderSource(vertexShader, vertexShaderScript); | |
| 1068 | + gl.compileShader(vertexShader); | |
| 1069 | + if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { | |
| 1070 | + this._opt.isDebug && console.log('Vertex shader failed to compile: ' + gl.getShaderInfoLog(vertexShader)); | |
| 1071 | + } | |
| 1072 | + | |
| 1073 | + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); | |
| 1074 | + gl.shaderSource(fragmentShader, fragmentShaderScript); | |
| 1075 | + gl.compileShader(fragmentShader); | |
| 1076 | + if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { | |
| 1077 | + this._opt.isDebug && console.log('Fragment shader failed to compile: ' + gl.getShaderInfoLog(fragmentShader)); | |
| 1078 | + } | |
| 1079 | + | |
| 1080 | + var program = gl.createProgram(); | |
| 1081 | + gl.attachShader(program, vertexShader); | |
| 1082 | + gl.attachShader(program, fragmentShader); | |
| 1083 | + gl.linkProgram(program); | |
| 1084 | + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { | |
| 1085 | + this._opt.isDebug && console.log('Program failed to compile: ' + gl.getProgramInfoLog(program)); | |
| 1086 | + } | |
| 1087 | + | |
| 1088 | + gl.useProgram(program); | |
| 1089 | + | |
| 1090 | + this._shaderProgram = program; | |
| 1091 | + }; | |
| 1092 | + | |
| 1093 | + /** | |
| 1094 | + * Initialize vertex buffers and attach to shader program | |
| 1095 | + */ | |
| 1096 | + Jessibuca.prototype._initBuffers = function () { | |
| 1097 | + var gl = this._contextGL; | |
| 1098 | + var program = this._shaderProgram; | |
| 1099 | + | |
| 1100 | + var vertexPosBuffer = gl.createBuffer(); | |
| 1101 | + gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer); | |
| 1102 | + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW); | |
| 1103 | + | |
| 1104 | + var vertexPosRef = gl.getAttribLocation(program, 'vertexPos'); | |
| 1105 | + gl.enableVertexAttribArray(vertexPosRef); | |
| 1106 | + gl.vertexAttribPointer(vertexPosRef, 2, gl.FLOAT, false, 0, 0); | |
| 1107 | + | |
| 1108 | + var texturePosBuffer = gl.createBuffer(); | |
| 1109 | + gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer); | |
| 1110 | + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW); | |
| 1111 | + | |
| 1112 | + var texturePosRef = gl.getAttribLocation(program, 'texturePos'); | |
| 1113 | + gl.enableVertexAttribArray(texturePosRef); | |
| 1114 | + gl.vertexAttribPointer(texturePosRef, 2, gl.FLOAT, false, 0, 0); | |
| 1115 | + | |
| 1116 | + this._texturePosBuffer = texturePosBuffer; | |
| 1117 | + }; | |
| 1118 | + | |
| 1119 | + /** | |
| 1120 | + * Initialize GL textures and attach to shader program | |
| 1121 | + */ | |
| 1122 | + Jessibuca.prototype._initTextures = function () { | |
| 1123 | + var gl = this._contextGL; | |
| 1124 | + var program = this._shaderProgram; | |
| 1125 | + | |
| 1126 | + var yTextureRef = this._initTexture(); | |
| 1127 | + var ySamplerRef = gl.getUniformLocation(program, 'ySampler'); | |
| 1128 | + gl.uniform1i(ySamplerRef, 0); | |
| 1129 | + this._yTextureRef = yTextureRef; | |
| 1130 | + | |
| 1131 | + var uTextureRef = this._initTexture(); | |
| 1132 | + var uSamplerRef = gl.getUniformLocation(program, 'uSampler'); | |
| 1133 | + gl.uniform1i(uSamplerRef, 1); | |
| 1134 | + this._uTextureRef = uTextureRef; | |
| 1135 | + | |
| 1136 | + var vTextureRef = this._initTexture(); | |
| 1137 | + var vSamplerRef = gl.getUniformLocation(program, 'vSampler'); | |
| 1138 | + gl.uniform1i(vSamplerRef, 2); | |
| 1139 | + this._vTextureRef = vTextureRef; | |
| 1140 | + }; | |
| 1141 | + | |
| 1142 | + /** | |
| 1143 | + * Create and configure a single texture | |
| 1144 | + */ | |
| 1145 | + Jessibuca.prototype._initTexture = function () { | |
| 1146 | + var gl = this._contextGL; | |
| 1147 | + | |
| 1148 | + var textureRef = gl.createTexture(); | |
| 1149 | + gl.bindTexture(gl.TEXTURE_2D, textureRef); | |
| 1150 | + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); | |
| 1151 | + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); | |
| 1152 | + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | |
| 1153 | + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | |
| 1154 | + gl.bindTexture(gl.TEXTURE_2D, null); | |
| 1155 | + | |
| 1156 | + return textureRef; | |
| 1157 | + }; | |
| 1158 | + | |
| 1159 | + /** | |
| 1160 | + * Draw picture data to the canvas. | |
| 1161 | + * If this object is using WebGL, the data must be an I420 formatted ArrayBuffer, | |
| 1162 | + * Otherwise, data must be an RGBA formatted ArrayBuffer. | |
| 1163 | + */ | |
| 1164 | + Jessibuca.prototype._drawNextOutputPicture = function (data) { | |
| 1165 | + if (this._contextGL) { | |
| 1166 | + this._drawNextOutputPictureGL(data); | |
| 1167 | + } else { | |
| 1168 | + this._drawNextOutputPictureRGBA(data); | |
| 1169 | + } | |
| 1170 | + }; | |
| 1171 | + | |
| 1172 | + /** | |
| 1173 | + * Draw the next output picture using WebGL | |
| 1174 | + */ | |
| 1175 | + Jessibuca.prototype._drawNextOutputPictureGL = function (data) { | |
| 1176 | + var gl = this._contextGL; | |
| 1177 | + var texturePosBuffer = this._texturePosBuffer; | |
| 1178 | + var yTextureRef = this._yTextureRef; | |
| 1179 | + var uTextureRef = this._uTextureRef; | |
| 1180 | + var vTextureRef = this._vTextureRef; | |
| 1181 | + var croppingParams = this.croppingParams | |
| 1182 | + var width = this._canvasElement.width | |
| 1183 | + var height = this._canvasElement.height | |
| 1184 | + if (croppingParams) { | |
| 1185 | + gl.viewport(0, 0, croppingParams.width, croppingParams.height); | |
| 1186 | + var tTop = croppingParams.top / height; | |
| 1187 | + var tLeft = croppingParams.left / width; | |
| 1188 | + var tBottom = croppingParams.height / height; | |
| 1189 | + var tRight = croppingParams.width / width; | |
| 1190 | + var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); | |
| 1191 | + | |
| 1192 | + gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer); | |
| 1193 | + gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW); | |
| 1194 | + } else { | |
| 1195 | + gl.viewport(0, 0, this._canvasElement.width, this._canvasElement.height); | |
| 1196 | + } | |
| 1197 | + gl.activeTexture(gl.TEXTURE0); | |
| 1198 | + gl.bindTexture(gl.TEXTURE_2D, yTextureRef); | |
| 1199 | + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data[0]); | |
| 1200 | + | |
| 1201 | + gl.activeTexture(gl.TEXTURE1); | |
| 1202 | + gl.bindTexture(gl.TEXTURE_2D, uTextureRef); | |
| 1203 | + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data[1]); | |
| 1204 | + | |
| 1205 | + gl.activeTexture(gl.TEXTURE2); | |
| 1206 | + gl.bindTexture(gl.TEXTURE_2D, vTextureRef); | |
| 1207 | + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data[2]); | |
| 1208 | + | |
| 1209 | + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); | |
| 1210 | + }; | |
| 1211 | + | |
| 1212 | + /** | |
| 1213 | + * Draw next output picture using ARGB data on a 2d canvas. | |
| 1214 | + */ | |
| 1215 | + Jessibuca.prototype._drawNextOutputPictureRGBA = function (data) { | |
| 1216 | + this.imageData.data.set(data); | |
| 1217 | + var croppingParams = this.croppingParams | |
| 1218 | + if (!croppingParams) { | |
| 1219 | + this.ctx2d.putImageData(this.imageData, 0, 0); | |
| 1220 | + } else { | |
| 1221 | + this.ctx2d.putImageData(this.imageData, -croppingParams.left, -croppingParams.top, 0, 0, croppingParams.width, croppingParams.height); | |
| 1222 | + } | |
| 1223 | + }; | |
| 1224 | + Jessibuca.prototype.ctx2d = null; | |
| 1225 | + Jessibuca.prototype.imageData = null; | |
| 1226 | + Jessibuca.prototype._initRGB = function (width, height) { | |
| 1227 | + this.ctx2d = this._canvasElement.getContext('2d'); | |
| 1228 | + this.imageData = this.ctx2d.getImageData(0, 0, width, height); | |
| 1229 | + this.clear = function () { | |
| 1230 | + this.ctx2d.clearRect(0, 0, width, height) | |
| 1231 | + }; | |
| 1232 | + }; | |
| 1233 | + | |
| 1234 | + Jessibuca.prototype.pause = function () { | |
| 1235 | + this._close(); | |
| 1236 | + if (this.loading) { | |
| 1237 | + _domToggle(this._doms.loadingDom, false); | |
| 1238 | + } | |
| 1239 | + this.recording = false; | |
| 1240 | + this.playing = false; | |
| 1241 | + }; | |
| 1242 | + | |
| 1243 | + Jessibuca.prototype._close = function () { | |
| 1244 | + if (this.audioInterval) { | |
| 1245 | + clearInterval(this.audioInterval) | |
| 1246 | + } | |
| 1247 | + delete this._playAudio | |
| 1248 | + this._decoderWorker.postMessage({cmd: "close"}) | |
| 1249 | + | |
| 1250 | + if (this._wakeLock) { | |
| 1251 | + this._wakeLock.release(); | |
| 1252 | + this._wakeLock = null; | |
| 1253 | + } | |
| 1254 | + | |
| 1255 | + // this._contextGL.clear(this._contextGL.COLOR_BUFFER_BIT); | |
| 1256 | + this._initCheckVariable(); | |
| 1257 | + } | |
| 1258 | + /** | |
| 1259 | + * destroy | |
| 1260 | + * @desc delete worker, | |
| 1261 | + */ | |
| 1262 | + Jessibuca.prototype.destroy = function () { | |
| 1263 | + // destroy | |
| 1264 | + this._decoderWorker.terminate() | |
| 1265 | + window.removeEventListener("resize", this._onresize); | |
| 1266 | + window.removeEventListener('fullscreenchange', this._onfullscreenchange); | |
| 1267 | + this._initCheckVariable(); | |
| 1268 | + this._clearCheckLoading(); | |
| 1269 | + this._off(); | |
| 1270 | + this._hasLoaded = false; | |
| 1271 | + // remove dom | |
| 1272 | + while (this._container.firstChild) { | |
| 1273 | + this._container.removeChild(this._container.firstChild); | |
| 1274 | + } | |
| 1275 | + if (this._wakeLock) { | |
| 1276 | + this._wakeLock.release(); | |
| 1277 | + } | |
| 1278 | + } | |
| 1279 | + | |
| 1280 | + /** | |
| 1281 | + * 清理画布为黑色背景 | |
| 1282 | + * 用于canvas重用进行多个流切换播放时,将上一个画面清理 | |
| 1283 | + * 避免后一个视频播放之前出现前一个视频最后一个画面 | |
| 1284 | + */ | |
| 1285 | + Jessibuca.prototype.clearView = function () { | |
| 1286 | + this._contextGL.clear(this._contextGL.COLOR_BUFFER_BIT); | |
| 1287 | + }; | |
| 1288 | + /** | |
| 1289 | + * play | |
| 1290 | + * @param url | |
| 1291 | + */ | |
| 1292 | + Jessibuca.prototype.play = function (url) { | |
| 1293 | + if (!this.playUrl && !url) { | |
| 1294 | + return; | |
| 1295 | + } | |
| 1296 | + var needDelay = false; | |
| 1297 | + if (url) { | |
| 1298 | + if (this.playUrl) { | |
| 1299 | + this._close(); | |
| 1300 | + needDelay = true; | |
| 1301 | + this._contextGL.clear(this._contextGL.COLOR_BUFFER_BIT); | |
| 1302 | + } | |
| 1303 | + this.loading = true; | |
| 1304 | + _domToggle(this._doms.bgDom, false); | |
| 1305 | + this._checkLoading(); | |
| 1306 | + this.playUrl = url; | |
| 1307 | + } else if (this.playUrl) { | |
| 1308 | + // retry | |
| 1309 | + if (this.loading) { | |
| 1310 | + this._hideBtns(); | |
| 1311 | + _domToggle(this._doms.fullscreenDom, true); | |
| 1312 | + _domToggle(this._doms.pauseDom, true); | |
| 1313 | + _domToggle(this._doms.loadingDom, true); | |
| 1314 | + this._checkLoading(); | |
| 1315 | + } else { | |
| 1316 | + this.playing = true; | |
| 1317 | + } | |
| 1318 | + } | |
| 1319 | + this._initCheckVariable(); | |
| 1320 | + | |
| 1321 | + if (needDelay) { | |
| 1322 | + var _this = this; | |
| 1323 | + setTimeout(function () { | |
| 1324 | + _this._decoderWorker.postMessage({cmd: "play", url: _this.playUrl, isWebGL: _this.isWebGL()}) | |
| 1325 | + }, 300); | |
| 1326 | + } else { | |
| 1327 | + this._decoderWorker.postMessage({cmd: "play", url: this.playUrl, isWebGL: this.isWebGL()}) | |
| 1328 | + } | |
| 1329 | + }; | |
| 1330 | + /** | |
| 1331 | + * has loaded | |
| 1332 | + * @returns {boolean} | |
| 1333 | + */ | |
| 1334 | + Jessibuca.prototype.hasLoaded = function () { | |
| 1335 | + return this._hasLoaded; | |
| 1336 | + }; | |
| 1337 | + | |
| 1338 | + Object.defineProperty(Jessibuca.prototype, "fullscreen", { | |
| 1339 | + set(value) { | |
| 1340 | + if (value) { | |
| 1341 | + if (!_checkFull()) { | |
| 1342 | + this._container.requestFullscreen(); | |
| 1343 | + } | |
| 1344 | + _domToggle(this._doms.minScreenDom, true); | |
| 1345 | + _domToggle(this._doms.fullscreenDom, false); | |
| 1346 | + } else { | |
| 1347 | + if (_checkFull()) { | |
| 1348 | + document.exitFullscreen(); | |
| 1349 | + } | |
| 1350 | + _domToggle(this._doms.minScreenDom, false); | |
| 1351 | + _domToggle(this._doms.fullscreenDom, true); | |
| 1352 | + } | |
| 1353 | + | |
| 1354 | + if (this._fullscreen !== value) { | |
| 1355 | + this.onFullscreen(value); | |
| 1356 | + this._trigger('fullscreen', value); | |
| 1357 | + } | |
| 1358 | + this._fullscreen = value; | |
| 1359 | + }, | |
| 1360 | + get() { | |
| 1361 | + return this._fullscreen; | |
| 1362 | + } | |
| 1363 | + }); | |
| 1364 | + | |
| 1365 | + Object.defineProperty(Jessibuca.prototype, 'playing', { | |
| 1366 | + set(value) { | |
| 1367 | + if (value) { | |
| 1368 | + _domToggle(this._doms.playBigDom, false); | |
| 1369 | + _domToggle(this._doms.playDom, false); | |
| 1370 | + _domToggle(this._doms.pauseDom, true); | |
| 1371 | + | |
| 1372 | + _domToggle(this._doms.screenshotsDom, true); | |
| 1373 | + _domToggle(this._doms.recordDom, true); | |
| 1374 | + if (this._quieting) { | |
| 1375 | + _domToggle(this._doms.quietAudioDom, true); | |
| 1376 | + _domToggle(this._doms.playAudioDom, false); | |
| 1377 | + } else { | |
| 1378 | + _domToggle(this._doms.quietAudioDom, false); | |
| 1379 | + _domToggle(this._doms.playAudioDom, true); | |
| 1380 | + } | |
| 1381 | + } else { | |
| 1382 | + this._doms.speedDom && (this._doms.speedDom.innerText = ''); | |
| 1383 | + if (this.playUrl) { | |
| 1384 | + _domToggle(this._doms.playDom, true); | |
| 1385 | + _domToggle(this._doms.playBigDom, true); | |
| 1386 | + _domToggle(this._doms.pauseDom, false); | |
| 1387 | + } | |
| 1388 | + | |
| 1389 | + // 在停止状态下录像,截屏,音量是非激活,只有播放,最大化时可点击 | |
| 1390 | + _domToggle(this._doms.recordDom, false); | |
| 1391 | + _domToggle(this._doms.recordingDom, false); | |
| 1392 | + _domToggle(this._doms.screenshotsDom, false); | |
| 1393 | + _domToggle(this._doms.quietAudioDom, false); | |
| 1394 | + _domToggle(this._doms.playAudioDom, false); | |
| 1395 | + } | |
| 1396 | + | |
| 1397 | + if (this._playing !== value) { | |
| 1398 | + if (value) { | |
| 1399 | + this.onPlay(); | |
| 1400 | + this._trigger('play'); | |
| 1401 | + } else { | |
| 1402 | + this.onPause(); | |
| 1403 | + this._trigger('pause'); | |
| 1404 | + } | |
| 1405 | + } | |
| 1406 | + this._playing = value; | |
| 1407 | + }, | |
| 1408 | + get() { | |
| 1409 | + return this._playing; | |
| 1410 | + } | |
| 1411 | + }); | |
| 1412 | + | |
| 1413 | + Object.defineProperty(Jessibuca.prototype, 'recording', { | |
| 1414 | + set(value) { | |
| 1415 | + if (value) { | |
| 1416 | + _domToggle(this._doms.recordDom, false); | |
| 1417 | + _domToggle(this._doms.recordingDom, true); | |
| 1418 | + } else { | |
| 1419 | + _domToggle(this._doms.recordDom, true); | |
| 1420 | + _domToggle(this._doms.recordingDom, false); | |
| 1421 | + | |
| 1422 | + } | |
| 1423 | + if (this._recording !== value) { | |
| 1424 | + this.onRecord(value); | |
| 1425 | + this._trigger('record', value); | |
| 1426 | + this._recording = value; | |
| 1427 | + } | |
| 1428 | + }, | |
| 1429 | + get() { | |
| 1430 | + return this._recording; | |
| 1431 | + } | |
| 1432 | + }); | |
| 1433 | + | |
| 1434 | + Object.defineProperty(Jessibuca.prototype, 'quieting', { | |
| 1435 | + set(value) { | |
| 1436 | + if (value) { | |
| 1437 | + _domToggle(this._doms.quietAudioDom, true); | |
| 1438 | + _domToggle(this._doms.playAudioDom, false); | |
| 1439 | + } else { | |
| 1440 | + _domToggle(this._doms.quietAudioDom, false); | |
| 1441 | + _domToggle(this._doms.playAudioDom, true); | |
| 1442 | + } | |
| 1443 | + if (this._quieting !== value) { | |
| 1444 | + this.onMute(value); | |
| 1445 | + this._trigger('mute', value); | |
| 1446 | + } | |
| 1447 | + this._quieting = value; | |
| 1448 | + }, | |
| 1449 | + get() { | |
| 1450 | + return this._quieting; | |
| 1451 | + } | |
| 1452 | + }); | |
| 1453 | + | |
| 1454 | + Object.defineProperty(Jessibuca.prototype, 'loading', { | |
| 1455 | + set(value) { | |
| 1456 | + if (value) { | |
| 1457 | + this._hideBtns(); | |
| 1458 | + _domToggle(this._doms.fullscreenDom, true); | |
| 1459 | + _domToggle(this._doms.pauseDom, true); | |
| 1460 | + _domToggle(this._doms.loadingDom, true); | |
| 1461 | + } else { | |
| 1462 | + this._initBtns(); | |
| 1463 | + } | |
| 1464 | + this._loading = value; | |
| 1465 | + }, | |
| 1466 | + get() { | |
| 1467 | + return this._loading; | |
| 1468 | + } | |
| 1469 | + }); | |
| 1470 | + | |
| 1471 | + /** | |
| 1472 | + * resize | |
| 1473 | + */ | |
| 1474 | + Jessibuca.prototype.resize = function () { | |
| 1475 | + var width = this._container.clientWidth; | |
| 1476 | + var height = this._container.clientHeight; | |
| 1477 | + if (this._showControl()) { | |
| 1478 | + height -= 38; | |
| 1479 | + } | |
| 1480 | + var resizeWidth = this._canvasElement.width; | |
| 1481 | + var resizeHeight = this._canvasElement.height; | |
| 1482 | + var wScale = width / resizeWidth; | |
| 1483 | + var hScale = height / resizeHeight; | |
| 1484 | + var scale = wScale > hScale ? hScale : wScale; | |
| 1485 | + if (!this._opt.isResize) { | |
| 1486 | + if (wScale !== hScale) { | |
| 1487 | + scale = wScale + ',' + hScale; | |
| 1488 | + } | |
| 1489 | + } | |
| 1490 | + // | |
| 1491 | + if (this._opt.isFullResize) { | |
| 1492 | + scale = wScale > hScale ? wScale : hScale; | |
| 1493 | + } | |
| 1494 | + | |
| 1495 | + this._opt.isDebug && console.log('wScale', wScale, 'hScale', hScale, 'scale', scale); | |
| 1496 | + this._canvasElement.style.transform = "scale(" + scale + ")" | |
| 1497 | + this._canvasElement.style.left = ((width - resizeWidth) / 2) + "px" | |
| 1498 | + this._canvasElement.style.top = ((height - resizeHeight) / 2) + "px" | |
| 1499 | + } | |
| 1500 | + | |
| 1501 | + Jessibuca.prototype._fullscreenchange = function () { | |
| 1502 | + this.fullscreen = _checkFull(); | |
| 1503 | + } | |
| 1504 | + | |
| 1505 | + /** | |
| 1506 | + * change buffer | |
| 1507 | + * @param buffer | |
| 1508 | + */ | |
| 1509 | + Jessibuca.prototype.changeBuffer = function (buffer) { | |
| 1510 | + this._stats.buf = Number(buffer) * 1000; | |
| 1511 | + this._decoderWorker.postMessage({cmd: "setVideoBuffer", time: Number(buffer)}); | |
| 1512 | + }; | |
| 1513 | + /** | |
| 1514 | + * 设置最大缓冲时长,单位秒,播放器会自动消除延迟。 | |
| 1515 | + * @param buffer | |
| 1516 | + */ | |
| 1517 | + Jessibuca.prototype.setBufferTime = function (buffer) { | |
| 1518 | + this.changeBuffer(buffer); | |
| 1519 | + }; | |
| 1520 | + | |
| 1521 | + /** | |
| 1522 | + * 设置音量大小,取值0.0 — 1.0 | |
| 1523 | + * 当为0.0时,完全无声 | |
| 1524 | + * 当为1.0时,最大音量,默认值 | |
| 1525 | + * @param volume | |
| 1526 | + */ | |
| 1527 | + Jessibuca.prototype.setVolume = function (volume) { | |
| 1528 | + if (this._gainNode) { | |
| 1529 | + this._gainNode.gain.setValueAtTime(volume, this._audioContext.currentTime); | |
| 1530 | + } | |
| 1531 | + }; | |
| 1532 | + | |
| 1533 | + /** | |
| 1534 | + * 开启屏幕常亮, 在play前调用 | |
| 1535 | + * 在手机浏览器上, canvas标签渲染视频并不会像video标签那样保持屏幕常亮 | |
| 1536 | + * H5目前在chrome\edge 84, android chrome 84及以上有原生亮屏API, 需要是https页面 | |
| 1537 | + * 其余平台为模拟实现,此时为兼容实现,并不保证所有浏览器都支持 | |
| 1538 | + */ | |
| 1539 | + Jessibuca.prototype.setKeepScreenOn = function () { | |
| 1540 | + this._opt.keepScreenOn = true; | |
| 1541 | + }; | |
| 1542 | + | |
| 1543 | + | |
| 1544 | + /** | |
| 1545 | + * set fullscreen | |
| 1546 | + * @param flag | |
| 1547 | + */ | |
| 1548 | + Jessibuca.prototype.setFullscreen = function (flag) { | |
| 1549 | + var fullscreen = !!flag; | |
| 1550 | + if (this.fullscreen !== fullscreen) { | |
| 1551 | + this.fullscreen = fullscreen; | |
| 1552 | + } | |
| 1553 | + }; | |
| 1554 | + | |
| 1555 | + function _now() { | |
| 1556 | + return new Date().getTime(); | |
| 1557 | + } | |
| 1558 | + | |
| 1559 | + Jessibuca.prototype._screenshot = function (filename, format, quality) { | |
| 1560 | + filename = filename || _now(); | |
| 1561 | + var formatType = { | |
| 1562 | + png: 'image/png', | |
| 1563 | + jpeg: 'image/jpeg', | |
| 1564 | + webp: 'image/webp' | |
| 1565 | + }; | |
| 1566 | + var encoderOptions = 0.92; | |
| 1567 | + | |
| 1568 | + if (typeof quality !== 'undefined') { | |
| 1569 | + encoderOptions = Number(quality); | |
| 1570 | + } | |
| 1571 | + | |
| 1572 | + var dataURL = this._canvasElement.toDataURL(formatType[format] || formatType.png, encoderOptions); | |
| 1573 | + _downloadImg(_dataURLToFile(dataURL), filename); | |
| 1574 | + } | |
| 1575 | + | |
| 1576 | + /** | |
| 1577 | + * 截图,调用后弹出下载框保存截图 | |
| 1578 | + * @param filename 保存的文件名 默认时间戳 | |
| 1579 | + * @param format 截图的格式,可选png或jpeg或者webp | |
| 1580 | + * @param quality 可选参数,当格式是jpeg或者webp时,压缩质量,取值0.0 ~ 1.0 | |
| 1581 | + */ | |
| 1582 | + Jessibuca.prototype.screenshot = function (filename, format, quality) { | |
| 1583 | + this._screenshot(filename, format, quality); | |
| 1584 | + }; | |
| 1585 | + | |
| 1586 | + | |
| 1587 | + var eventSplitter = /\s+/; | |
| 1588 | + | |
| 1589 | + // Execute callbacks | |
| 1590 | + function _callEach(list, args, context) { | |
| 1591 | + if (list) { | |
| 1592 | + for (var i = 0, len = list.length; i < len; i += 1) { | |
| 1593 | + list[i].apply(context, args); | |
| 1594 | + } | |
| 1595 | + } | |
| 1596 | + } | |
| 1597 | + | |
| 1598 | + /** | |
| 1599 | + * | |
| 1600 | + * @param events | |
| 1601 | + * @param callback | |
| 1602 | + * @returns {Jessibuca} | |
| 1603 | + */ | |
| 1604 | + Jessibuca.prototype.on = function (events, callback) { | |
| 1605 | + var cache, event, list; | |
| 1606 | + if (!callback) return this; | |
| 1607 | + cache = this.__events || (this.__events = {}); | |
| 1608 | + events = events.split(eventSplitter); | |
| 1609 | + while (event = events.shift()) { | |
| 1610 | + list = cache[event] || (cache[event] = []); | |
| 1611 | + list.push(callback); | |
| 1612 | + } | |
| 1613 | + return this; | |
| 1614 | + }; | |
| 1615 | + /** | |
| 1616 | + * | |
| 1617 | + * @param events | |
| 1618 | + * @param callback | |
| 1619 | + * @returns {Jessibuca} | |
| 1620 | + * @private | |
| 1621 | + */ | |
| 1622 | + Jessibuca.prototype._off = function () { | |
| 1623 | + var cache; | |
| 1624 | + if (!(cache = this.__events)) return this; | |
| 1625 | + delete this.__events; | |
| 1626 | + return this; | |
| 1627 | + }; | |
| 1628 | + | |
| 1629 | + /** | |
| 1630 | + * | |
| 1631 | + * @param events | |
| 1632 | + * @returns {Jessibuca} | |
| 1633 | + * @private | |
| 1634 | + */ | |
| 1635 | + Jessibuca.prototype._trigger = function (events) { | |
| 1636 | + var cache, event, all, list, i, len, rest = [], args; | |
| 1637 | + if (!(cache = this.__events)) return this; | |
| 1638 | + events = events.split(eventSplitter); | |
| 1639 | + // Fill up `rest` with the callback arguments. Since we're only copying | |
| 1640 | + // the tail of `arguments`, a loop is much faster than Array#slice. | |
| 1641 | + for (i = 1, len = arguments.length; i < len; i++) { | |
| 1642 | + rest[i - 1] = arguments[i]; | |
| 1643 | + } | |
| 1644 | + // For each event, walk through the list of callbacks twice, first to | |
| 1645 | + // trigger the event, then to trigger any `"all"` callbacks. | |
| 1646 | + while (event = events.shift()) { | |
| 1647 | + if (list = cache[event]) list = list.slice(); | |
| 1648 | + // Execute event callbacks. | |
| 1649 | + _callEach(list, rest, this); | |
| 1650 | + } | |
| 1651 | + return this; | |
| 1652 | + } | |
| 1653 | + | |
| 1654 | + if (typeof define === 'function') { | |
| 1655 | + define(function () { | |
| 1656 | + return Jessibuca; | |
| 1657 | + }); | |
| 1658 | + } else if (typeof exports !== 'undefined') { | |
| 1659 | + module.exports = Jessibuca; | |
| 1660 | + } else { | |
| 1661 | + window.Jessibuca = Jessibuca; | |
| 1662 | + } | |
| 1663 | +})(); | ... | ... |
src/api/car/carStructureApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +// 查询楼栋和单元树形数据 | |
| 5 | +export function queryFloorAndUnits(params) { | |
| 6 | + return new Promise((resolve, reject) => { | |
| 7 | + const communityId = getCommunityId() | |
| 8 | + request({ | |
| 9 | + url: '/floor.queryFloorAndUnits', | |
| 10 | + method: 'get', | |
| 11 | + params: { | |
| 12 | + ...params, | |
| 13 | + communityId | |
| 14 | + } | |
| 15 | + }).then(response => { | |
| 16 | + const res = response.data | |
| 17 | + resolve(res) | |
| 18 | + }).catch(error => { | |
| 19 | + reject(error) | |
| 20 | + }) | |
| 21 | + }) | |
| 22 | +} | |
| 23 | + | |
| 24 | +// 查询车位结构列表 | |
| 25 | +export function listCarStructure(params) { | |
| 26 | + return new Promise((resolve, reject) => { | |
| 27 | + const communityId = getCommunityId() | |
| 28 | + request({ | |
| 29 | + url: '/car.listCarStructure', | |
| 30 | + method: 'get', | |
| 31 | + params: { | |
| 32 | + ...params, | |
| 33 | + communityId | |
| 34 | + } | |
| 35 | + }).then(response => { | |
| 36 | + const res = response.data | |
| 37 | + resolve({ | |
| 38 | + data: res.data, | |
| 39 | + total: res.total | |
| 40 | + }) | |
| 41 | + }).catch(error => { | |
| 42 | + reject(error) | |
| 43 | + }) | |
| 44 | + }) | |
| 45 | +} | |
| 46 | + | |
| 47 | +// 获取当前小区ID | |
| 48 | +export function getCurrentCommunityId() { | |
| 49 | + return getCommunityId() | |
| 50 | +} | |
| 0 | 51 | \ No newline at end of file | ... | ... |
src/api/fee/payFeeQrcodeApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +// 获取支付二维码列表 | |
| 4 | +export function listPayFeeQrcode(params) { | |
| 5 | + return new Promise((resolve, reject) => { | |
| 6 | + request({ | |
| 7 | + url: '/payFeeQrcode.listPayFeeQrcode', | |
| 8 | + method: 'get', | |
| 9 | + params | |
| 10 | + }).then(response => { | |
| 11 | + const res = response.data | |
| 12 | + resolve(res) | |
| 13 | + }).catch(error => { | |
| 14 | + reject(error) | |
| 15 | + }) | |
| 16 | + }) | |
| 17 | +} | |
| 18 | + | |
| 19 | +// 添加支付二维码 | |
| 20 | +export function savePayFeeQrcode(data) { | |
| 21 | + return new Promise((resolve, reject) => { | |
| 22 | + request({ | |
| 23 | + url: '/payFeeQrcode.savePayFeeQrcode', | |
| 24 | + method: 'post', | |
| 25 | + data | |
| 26 | + }).then(response => { | |
| 27 | + const res = response.data | |
| 28 | + resolve(res) | |
| 29 | + }).catch(error => { | |
| 30 | + reject(error) | |
| 31 | + }) | |
| 32 | + }) | |
| 33 | +} | |
| 34 | + | |
| 35 | +// 更新支付二维码 | |
| 36 | +export function updatePayFeeQrcode(data) { | |
| 37 | + return new Promise((resolve, reject) => { | |
| 38 | + request({ | |
| 39 | + url: '/payFeeQrcode.updatePayFeeQrcode', | |
| 40 | + method: 'post', | |
| 41 | + data | |
| 42 | + }).then(response => { | |
| 43 | + const res = response.data | |
| 44 | + resolve(res) | |
| 45 | + }).catch(error => { | |
| 46 | + reject(error) | |
| 47 | + }) | |
| 48 | + }) | |
| 49 | +} | |
| 50 | + | |
| 51 | +// 删除支付二维码 | |
| 52 | +export function deletePayFeeQrcode(data) { | |
| 53 | + return new Promise((resolve, reject) => { | |
| 54 | + request({ | |
| 55 | + url: '/payFeeQrcode.deletePayFeeQrcode', | |
| 56 | + method: 'post', | |
| 57 | + data | |
| 58 | + }).then(response => { | |
| 59 | + const res = response.data | |
| 60 | + resolve(res) | |
| 61 | + }).catch(error => { | |
| 62 | + reject(error) | |
| 63 | + }) | |
| 64 | + }) | |
| 65 | +} | |
| 0 | 66 | \ No newline at end of file | ... | ... |
src/api/machine/videoControlApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +// 获取监控区域列表 | |
| 4 | +export function getMonitorAreas(params) { | |
| 5 | + return new Promise((resolve, reject) => { | |
| 6 | + request({ | |
| 7 | + url: '/iot.getOpenApi', | |
| 8 | + method: 'get', | |
| 9 | + params: { | |
| 10 | + ...params, | |
| 11 | + iotApiCode: 'listMonitorAreaBmoImpl' | |
| 12 | + } | |
| 13 | + }).then(response => { | |
| 14 | + const res = response.data | |
| 15 | + resolve(res) | |
| 16 | + }).catch(error => { | |
| 17 | + reject(error) | |
| 18 | + }) | |
| 19 | + }) | |
| 20 | +} | |
| 21 | + | |
| 22 | +// 获取监控设备列表 | |
| 23 | +export function getMonitorMachines(params) { | |
| 24 | + return new Promise((resolve, reject) => { | |
| 25 | + request({ | |
| 26 | + url: '/iot.getOpenApi', | |
| 27 | + method: 'get', | |
| 28 | + params: { | |
| 29 | + ...params, | |
| 30 | + iotApiCode: 'listMonitorMachineBmoImpl' | |
| 31 | + } | |
| 32 | + }).then(response => { | |
| 33 | + const res = response.data | |
| 34 | + resolve(res) | |
| 35 | + }).catch(error => { | |
| 36 | + reject(error) | |
| 37 | + }) | |
| 38 | + }) | |
| 39 | +} | |
| 40 | + | |
| 41 | +// 获取视频播放地址 | |
| 42 | +export function getPlayVideoUrl(params) { | |
| 43 | + return new Promise((resolve, reject) => { | |
| 44 | + request({ | |
| 45 | + url: '/iot.getOpenApi', | |
| 46 | + method: 'get', | |
| 47 | + params: { | |
| 48 | + ...params, | |
| 49 | + iotApiCode: 'getPlayVideoUrlBmoImpl' | |
| 50 | + } | |
| 51 | + }).then(response => { | |
| 52 | + const res = response.data | |
| 53 | + resolve(res) | |
| 54 | + }).catch(error => { | |
| 55 | + reject(error) | |
| 56 | + }) | |
| 57 | + }) | |
| 58 | +} | |
| 0 | 59 | \ No newline at end of file | ... | ... |
src/api/room/listPropertyRightRegistrationDetailApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * 获取产权登记详情列表 | |
| 5 | + * @param {Object} params 查询参数 | |
| 6 | + * @returns {Promise} | |
| 7 | + */ | |
| 8 | +export function listPropertyRightRegistrationDetail(params) { | |
| 9 | + return new Promise((resolve, reject) => { | |
| 10 | + request({ | |
| 11 | + url: '/propertyRightRegistrationDetail.listPropertyRightRegistrationDetail', | |
| 12 | + method: 'get', | |
| 13 | + params | |
| 14 | + }).then(response => { | |
| 15 | + const res = response.data | |
| 16 | + resolve(res) | |
| 17 | + }).catch(error => { | |
| 18 | + reject(error) | |
| 19 | + }) | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * 更新产权登记详情 | |
| 25 | + * @param {Object} data 更新数据 | |
| 26 | + * @returns {Promise} | |
| 27 | + */ | |
| 28 | +export function updatePropertyRightRegistrationDetail(data) { | |
| 29 | + return new Promise((resolve, reject) => { | |
| 30 | + request({ | |
| 31 | + url: '/propertyRightRegistrationDetail.updatePropertyRightRegistrationDetail', | |
| 32 | + method: 'post', | |
| 33 | + data | |
| 34 | + }).then(response => { | |
| 35 | + const res = response.data | |
| 36 | + resolve(res) | |
| 37 | + }).catch(error => { | |
| 38 | + reject(error) | |
| 39 | + }) | |
| 40 | + }) | |
| 41 | +} | |
| 42 | + | |
| 43 | +/** | |
| 44 | + * 获取产权登记详情 | |
| 45 | + * @param {String} prrdId 详情ID | |
| 46 | + * @returns {Promise} | |
| 47 | + */ | |
| 48 | +export function getPropertyRightRegistrationDetail(prrdId) { | |
| 49 | + return new Promise((resolve, reject) => { | |
| 50 | + request({ | |
| 51 | + url: '/propertyRightRegistrationDetail.getPropertyRightRegistrationDetail', | |
| 52 | + method: 'get', | |
| 53 | + params: { prrdId } | |
| 54 | + }).then(response => { | |
| 55 | + const res = response.data | |
| 56 | + resolve(res) | |
| 57 | + }).catch(error => { | |
| 58 | + reject(error) | |
| 59 | + }) | |
| 60 | + }) | |
| 61 | +} | |
| 0 | 62 | \ No newline at end of file | ... | ... |
src/api/room/propertyRightRegistrationManageApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * 获取产权登记列表 | |
| 6 | + * @param {Object} params 查询参数 | |
| 7 | + * @returns {Promise} | |
| 8 | + */ | |
| 9 | +export function listPropertyRightRegistration(params) { | |
| 10 | + return new Promise((resolve, reject) => { | |
| 11 | + const communityId = getCommunityId() | |
| 12 | + request({ | |
| 13 | + url: '/propertyRightRegistration.listPropertyRightRegistration', | |
| 14 | + method: 'get', | |
| 15 | + params: { | |
| 16 | + ...params, | |
| 17 | + communityId | |
| 18 | + } | |
| 19 | + }).then(response => { | |
| 20 | + const res = response.data | |
| 21 | + resolve({ | |
| 22 | + data: res.data, | |
| 23 | + total: res.total, | |
| 24 | + records: res.records | |
| 25 | + }) | |
| 26 | + }).catch(error => { | |
| 27 | + reject(error) | |
| 28 | + }) | |
| 29 | + }) | |
| 30 | +} | |
| 31 | + | |
| 32 | +/** | |
| 33 | + * 添加产权登记 | |
| 34 | + * @param {Object} data 产权登记数据 | |
| 35 | + * @returns {Promise} | |
| 36 | + */ | |
| 37 | +export function savePropertyRightRegistration(data) { | |
| 38 | + return new Promise((resolve, reject) => { | |
| 39 | + const communityId = getCommunityId() | |
| 40 | + request({ | |
| 41 | + url: '/propertyRightRegistration.savePropertyRightRegistration', | |
| 42 | + method: 'post', | |
| 43 | + data: { | |
| 44 | + ...data, | |
| 45 | + communityId | |
| 46 | + } | |
| 47 | + }).then(response => { | |
| 48 | + const res = response.data | |
| 49 | + resolve(res) | |
| 50 | + }).catch(error => { | |
| 51 | + reject(error) | |
| 52 | + }) | |
| 53 | + }) | |
| 54 | +} | |
| 55 | + | |
| 56 | +/** | |
| 57 | + * 更新产权登记 | |
| 58 | + * @param {Object} data 更新数据 | |
| 59 | + * @returns {Promise} | |
| 60 | + */ | |
| 61 | +export function updatePropertyRightRegistration(data) { | |
| 62 | + return new Promise((resolve, reject) => { | |
| 63 | + const communityId = getCommunityId() | |
| 64 | + request({ | |
| 65 | + url: '/propertyRightRegistration.updatePropertyRightRegistration', | |
| 66 | + method: 'post', | |
| 67 | + data: { | |
| 68 | + ...data, | |
| 69 | + communityId | |
| 70 | + } | |
| 71 | + }).then(response => { | |
| 72 | + const res = response.data | |
| 73 | + resolve(res) | |
| 74 | + }).catch(error => { | |
| 75 | + reject(error) | |
| 76 | + }) | |
| 77 | + }) | |
| 78 | +} | |
| 79 | + | |
| 80 | +/** | |
| 81 | + * 删除产权登记 | |
| 82 | + * @param {String} prrId 产权登记ID | |
| 83 | + * @returns {Promise} | |
| 84 | + */ | |
| 85 | +export function deletePropertyRightRegistration(prrId) { | |
| 86 | + return new Promise((resolve, reject) => { | |
| 87 | + const communityId = getCommunityId() | |
| 88 | + request({ | |
| 89 | + url: '/propertyRightRegistration.deletePropertyRightRegistration', | |
| 90 | + method: 'post', | |
| 91 | + data: { | |
| 92 | + prrId, | |
| 93 | + communityId | |
| 94 | + } | |
| 95 | + }).then(response => { | |
| 96 | + const res = response.data | |
| 97 | + resolve(res) | |
| 98 | + }).catch(error => { | |
| 99 | + reject(error) | |
| 100 | + }) | |
| 101 | + }) | |
| 102 | +} | |
| 0 | 103 | \ No newline at end of file | ... | ... |
src/api/room/roomStructureApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * 查询楼层和单元树数据 | |
| 6 | + * @returns {Promise} | |
| 7 | + */ | |
| 8 | +export function queryFloorAndUnits() { | |
| 9 | + return new Promise((resolve, reject) => { | |
| 10 | + request({ | |
| 11 | + url: '/floor.queryFloorAndUnits', | |
| 12 | + method: 'get', | |
| 13 | + params: { | |
| 14 | + communityId: getCommunityId() | |
| 15 | + } | |
| 16 | + }).then(response => { | |
| 17 | + resolve(response.data) | |
| 18 | + }).catch(error => { | |
| 19 | + reject(error) | |
| 20 | + }) | |
| 21 | + }) | |
| 22 | +} | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * 查询房间结构列表 | |
| 26 | + * @param {Object} params 查询参数 | |
| 27 | + * @param {number} params.page 页码 | |
| 28 | + * @param {number} params.row 每页条数 | |
| 29 | + * @param {string} params.unitId 单元ID | |
| 30 | + * @returns {Promise} | |
| 31 | + */ | |
| 32 | +export function listRoomStructure(params) { | |
| 33 | + return new Promise((resolve, reject) => { | |
| 34 | + request({ | |
| 35 | + url: '/room.listRoomStructure', | |
| 36 | + method: 'get', | |
| 37 | + params: { | |
| 38 | + ...params, | |
| 39 | + communityId: getCommunityId() | |
| 40 | + } | |
| 41 | + }).then(response => { | |
| 42 | + const res = response.data | |
| 43 | + resolve({ | |
| 44 | + data: res.data, | |
| 45 | + total: res.total | |
| 46 | + }) | |
| 47 | + }).catch(error => { | |
| 48 | + reject(error) | |
| 49 | + }) | |
| 50 | + }) | |
| 51 | +} | |
| 52 | + | |
| 53 | +/** | |
| 54 | + * 获取房间状态字典 | |
| 55 | + * @returns {Promise} | |
| 56 | + */ | |
| 57 | +export function getRoomStateDict() { | |
| 58 | + return new Promise((resolve, reject) => { | |
| 59 | + request({ | |
| 60 | + url: '/dict.getDict', | |
| 61 | + method: 'get', | |
| 62 | + params: { | |
| 63 | + dictType: 'room_state' | |
| 64 | + } | |
| 65 | + }).then(response => { | |
| 66 | + resolve(response.data) | |
| 67 | + }).catch(error => { | |
| 68 | + reject(error) | |
| 69 | + }) | |
| 70 | + }) | |
| 71 | +} | |
| 0 | 72 | \ No newline at end of file | ... | ... |
src/api/system/assetImportLogApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * Query asset import logs | |
| 5 | + * @param {Object} params | |
| 6 | + * @param {number} params.page - Page number | |
| 7 | + * @param {number} params.row - Page size | |
| 8 | + * @param {string} params.communityId - Community ID | |
| 9 | + * @returns {Promise} Promise object represents the import logs | |
| 10 | + */ | |
| 11 | +export function queryAssetImportLog(params) { | |
| 12 | + return new Promise((resolve, reject) => { | |
| 13 | + request({ | |
| 14 | + url: '/assetImportLog/queryAssetImportLog', | |
| 15 | + method: 'get', | |
| 16 | + params | |
| 17 | + }).then(response => { | |
| 18 | + const res = response.data | |
| 19 | + resolve(res) | |
| 20 | + }).catch(error => { | |
| 21 | + reject(error) | |
| 22 | + }) | |
| 23 | + }) | |
| 24 | +} | |
| 0 | 25 | \ No newline at end of file | ... | ... |
src/api/system/assetImportLogDetailApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * 查询资产导入日志详情 | |
| 5 | + * @param {Object} params 查询参数 | |
| 6 | + * @returns {Promise} 请求Promise | |
| 7 | + */ | |
| 8 | +export function queryAssetImportLogDetail(params) { | |
| 9 | + return new Promise((resolve, reject) => { | |
| 10 | + request({ | |
| 11 | + url: '/assetImportLogDetail/queryAssetImportLogDetail', | |
| 12 | + method: 'get', | |
| 13 | + params | |
| 14 | + }).then(response => { | |
| 15 | + const res = response.data | |
| 16 | + resolve(res) | |
| 17 | + }).catch(error => { | |
| 18 | + reject(error) | |
| 19 | + }) | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * 查询资产导入日志类型 | |
| 25 | + * @param {Object} params 查询参数 | |
| 26 | + * @returns {Promise} 请求Promise | |
| 27 | + */ | |
| 28 | +export function queryAssetImportLogType(params) { | |
| 29 | + return new Promise((resolve, reject) => { | |
| 30 | + request({ | |
| 31 | + url: '/log.queryAssetImportLogType', | |
| 32 | + method: 'get', | |
| 33 | + params | |
| 34 | + }).then(response => { | |
| 35 | + const res = response.data | |
| 36 | + resolve(res) | |
| 37 | + }).catch(error => { | |
| 38 | + reject(error) | |
| 39 | + }) | |
| 40 | + }) | |
| 41 | +} | |
| 0 | 42 | \ No newline at end of file | ... | ... |
src/api/system/downloadTempFileApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +// 获取下载文件列表 | |
| 5 | +export function listUserDownloadFile(params) { | |
| 6 | + return new Promise((resolve, reject) => { | |
| 7 | + request({ | |
| 8 | + url: '/userDownloadFile.listUserDownloadFile', | |
| 9 | + method: 'get', | |
| 10 | + params: { | |
| 11 | + ...params, | |
| 12 | + communityId: getCommunityId() | |
| 13 | + } | |
| 14 | + }).then(response => { | |
| 15 | + const res = response.data | |
| 16 | + resolve(res) | |
| 17 | + }).catch(error => { | |
| 18 | + reject(error) | |
| 19 | + }) | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +// 删除下载文件 | |
| 24 | +export function deleteUserDownloadFile(data) { | |
| 25 | + return new Promise((resolve, reject) => { | |
| 26 | + request({ | |
| 27 | + url: '/userDownloadFile.deleteUserDownloadFile', | |
| 28 | + method: 'post', | |
| 29 | + data: { | |
| 30 | + ...data, | |
| 31 | + communityId: getCommunityId() | |
| 32 | + } | |
| 33 | + }).then(response => { | |
| 34 | + const res = response.data | |
| 35 | + resolve(res) | |
| 36 | + }).catch(error => { | |
| 37 | + reject(error) | |
| 38 | + }) | |
| 39 | + }) | |
| 40 | +} | |
| 0 | 41 | \ No newline at end of file | ... | ... |
src/api/system/feePrintPageManageApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +// 获取收据模板列表 | |
| 5 | +export function listFeePrintPage(params) { | |
| 6 | + return new Promise((resolve, reject) => { | |
| 7 | + request({ | |
| 8 | + url: '/feePrintPage.listFeePrintPage', | |
| 9 | + method: 'get', | |
| 10 | + params: { | |
| 11 | + ...params, | |
| 12 | + communityId: getCommunityId() | |
| 13 | + } | |
| 14 | + }).then(response => { | |
| 15 | + const res = response.data | |
| 16 | + resolve(res) | |
| 17 | + }).catch(error => { | |
| 18 | + reject(error) | |
| 19 | + }) | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +// 获取收据页面模板列表 | |
| 24 | +export function listFeePrintPageTemplate(params) { | |
| 25 | + return new Promise((resolve, reject) => { | |
| 26 | + request({ | |
| 27 | + url: '/feePrintPageTemplate.listFeePrintPageTemplate', | |
| 28 | + method: 'get', | |
| 29 | + params: { | |
| 30 | + ...params, | |
| 31 | + communityId: getCommunityId() | |
| 32 | + } | |
| 33 | + }).then(response => { | |
| 34 | + const res = response.data | |
| 35 | + resolve(res) | |
| 36 | + }).catch(error => { | |
| 37 | + reject(error) | |
| 38 | + }) | |
| 39 | + }) | |
| 40 | +} | |
| 41 | + | |
| 42 | +// 添加收据模板 | |
| 43 | +export function saveFeePrintPage(data) { | |
| 44 | + return new Promise((resolve, reject) => { | |
| 45 | + request({ | |
| 46 | + url: '/feePrintPage.saveFeePrintPage', | |
| 47 | + method: 'post', | |
| 48 | + data: { | |
| 49 | + ...data, | |
| 50 | + communityId: getCommunityId() | |
| 51 | + } | |
| 52 | + }).then(response => { | |
| 53 | + const res = response.data | |
| 54 | + resolve(res) | |
| 55 | + }).catch(error => { | |
| 56 | + reject(error) | |
| 57 | + }) | |
| 58 | + }) | |
| 59 | +} | |
| 60 | + | |
| 61 | +// 更新收据模板 | |
| 62 | +export function updateFeePrintPage(data) { | |
| 63 | + return new Promise((resolve, reject) => { | |
| 64 | + request({ | |
| 65 | + url: '/feePrintPage.updateFeePrintPage', | |
| 66 | + method: 'post', | |
| 67 | + data: { | |
| 68 | + ...data, | |
| 69 | + communityId: getCommunityId() | |
| 70 | + } | |
| 71 | + }).then(response => { | |
| 72 | + const res = response.data | |
| 73 | + resolve(res) | |
| 74 | + }).catch(error => { | |
| 75 | + reject(error) | |
| 76 | + }) | |
| 77 | + }) | |
| 78 | +} | |
| 79 | + | |
| 80 | +// 删除收据模板 | |
| 81 | +export function deleteFeePrintPage(data) { | |
| 82 | + return new Promise((resolve, reject) => { | |
| 83 | + request({ | |
| 84 | + url: '/feePrintPage.deleteFeePrintPage', | |
| 85 | + method: 'post', | |
| 86 | + data: { | |
| 87 | + ...data, | |
| 88 | + communityId: getCommunityId() | |
| 89 | + } | |
| 90 | + }).then(response => { | |
| 91 | + const res = response.data | |
| 92 | + resolve(res) | |
| 93 | + }).catch(error => { | |
| 94 | + reject(error) | |
| 95 | + }) | |
| 96 | + }) | |
| 97 | +} | |
| 98 | + | |
| 99 | +// 更新收据模板状态 | |
| 100 | +export function updateFeePrintPageState(data) { | |
| 101 | + return new Promise((resolve, reject) => { | |
| 102 | + request({ | |
| 103 | + url: '/feePrintPage.updateFeePrintPage', | |
| 104 | + method: 'post', | |
| 105 | + data: { | |
| 106 | + ...data, | |
| 107 | + communityId: getCommunityId() | |
| 108 | + } | |
| 109 | + }).then(response => { | |
| 110 | + const res = response.data | |
| 111 | + resolve(res) | |
| 112 | + }).catch(error => { | |
| 113 | + reject(error) | |
| 114 | + }) | |
| 115 | + }) | |
| 116 | +} | |
| 0 | 117 | \ No newline at end of file | ... | ... |
src/components/TestScript.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="test-script"> | |
| 3 | + <h3>全局脚本测试</h3> | |
| 4 | + <div class="test-results"> | |
| 5 | + <p><strong>Qs 库状态:</strong> {{ qsStatus }}</p> | |
| 6 | + <p><strong>Qs 版本:</strong> {{ qsVersion }}</p> | |
| 7 | + <p><strong>测试结果:</strong> {{ testResult }}</p> | |
| 8 | + </div> | |
| 9 | + <el-button @click="testQs" type="primary">测试 Qs 功能</el-button> | |
| 10 | + </div> | |
| 11 | +</template> | |
| 12 | + | |
| 13 | +<script> | |
| 14 | +export default { | |
| 15 | + name: 'TestScript', | |
| 16 | + data() { | |
| 17 | + return { | |
| 18 | + qsStatus: '检查中...', | |
| 19 | + qsVersion: '未知', | |
| 20 | + testResult: '未测试' | |
| 21 | + } | |
| 22 | + }, | |
| 23 | + mounted() { | |
| 24 | + this.checkQsStatus() | |
| 25 | + }, | |
| 26 | + methods: { | |
| 27 | + checkQsStatus() { | |
| 28 | + // 检查全局 Qs | |
| 29 | + if (typeof window.Qs !== 'undefined') { | |
| 30 | + this.qsStatus = '已加载' | |
| 31 | + this.qsVersion = window.Qs.VERSION || '未知版本' | |
| 32 | + } else { | |
| 33 | + this.qsStatus = '未加载' | |
| 34 | + } | |
| 35 | + | |
| 36 | + // 检查 Vue 原型上的 Qs | |
| 37 | + if (this.$qs) { | |
| 38 | + this.qsStatus += ' (Vue 原型已挂载)' | |
| 39 | + } | |
| 40 | + }, | |
| 41 | + testQs() { | |
| 42 | + try { | |
| 43 | + // 测试 Qs 功能 | |
| 44 | + const testObj = { name: 'test', value: 123 } | |
| 45 | + const queryString = this.$qs.stringify(testObj) | |
| 46 | + const parsedObj = this.$qs.parse(queryString) | |
| 47 | + | |
| 48 | + this.testResult = `成功! 序列化: ${queryString}, 解析: ${JSON.stringify(parsedObj)}` | |
| 49 | + console.log('Qs 测试成功:', { queryString, parsedObj }) | |
| 50 | + } catch (error) { | |
| 51 | + this.testResult = `失败: ${error.message}` | |
| 52 | + console.error('Qs 测试失败:', error) | |
| 53 | + } | |
| 54 | + } | |
| 55 | + } | |
| 56 | +} | |
| 57 | +</script> | |
| 58 | + | |
| 59 | +<style scoped> | |
| 60 | +.test-script { | |
| 61 | + padding: 20px; | |
| 62 | + border: 1px solid #ddd; | |
| 63 | + border-radius: 4px; | |
| 64 | + margin: 20px; | |
| 65 | +} | |
| 66 | + | |
| 67 | +.test-results { | |
| 68 | + margin: 15px 0; | |
| 69 | + padding: 10px; | |
| 70 | + background-color: #f5f5f5; | |
| 71 | + border-radius: 4px; | |
| 72 | +} | |
| 73 | + | |
| 74 | +.test-results p { | |
| 75 | + margin: 5px 0; | |
| 76 | +} | |
| 77 | +</style> | |
| 0 | 78 | \ No newline at end of file | ... | ... |
src/components/fee/addPayFeeQrcode.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('payFeeQrcode.add.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="50%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <el-form | |
| 9 | + ref="form" | |
| 10 | + :model="form" | |
| 11 | + :rules="rules" | |
| 12 | + label-width="120px" | |
| 13 | + > | |
| 14 | + <el-form-item | |
| 15 | + :label="$t('payFeeQrcode.form.qrcodeName')" | |
| 16 | + prop="qrcodeName" | |
| 17 | + > | |
| 18 | + <el-input | |
| 19 | + v-model="form.qrcodeName" | |
| 20 | + :placeholder="$t('payFeeQrcode.placeholder.qrcodeName')" | |
| 21 | + /> | |
| 22 | + </el-form-item> | |
| 23 | + | |
| 24 | + <el-form-item | |
| 25 | + :label="$t('payFeeQrcode.form.queryWay')" | |
| 26 | + prop="queryWay" | |
| 27 | + > | |
| 28 | + <el-select | |
| 29 | + v-model="form.queryWay" | |
| 30 | + :placeholder="$t('payFeeQrcode.placeholder.queryWay')" | |
| 31 | + style="width:100%" | |
| 32 | + > | |
| 33 | + <el-option | |
| 34 | + v-for="item in queryWayOptions" | |
| 35 | + :key="item.value" | |
| 36 | + :label="item.label" | |
| 37 | + :value="item.value" | |
| 38 | + /> | |
| 39 | + </el-select> | |
| 40 | + </el-form-item> | |
| 41 | + | |
| 42 | + <el-form-item | |
| 43 | + :label="$t('payFeeQrcode.form.smsValidate')" | |
| 44 | + prop="smsValidate" | |
| 45 | + > | |
| 46 | + <el-select | |
| 47 | + v-model="form.smsValidate" | |
| 48 | + :placeholder="$t('payFeeQrcode.placeholder.smsValidate')" | |
| 49 | + style="width:100%" | |
| 50 | + > | |
| 51 | + <el-option | |
| 52 | + :label="$t('common.yes')" | |
| 53 | + value="ON" | |
| 54 | + /> | |
| 55 | + <el-option | |
| 56 | + :label="$t('common.no')" | |
| 57 | + value="OFF" | |
| 58 | + /> | |
| 59 | + </el-select> | |
| 60 | + </el-form-item> | |
| 61 | + | |
| 62 | + <el-form-item | |
| 63 | + :label="$t('payFeeQrcode.form.customFee')" | |
| 64 | + prop="customFee" | |
| 65 | + > | |
| 66 | + <el-select | |
| 67 | + v-model="form.customFee" | |
| 68 | + :placeholder="$t('payFeeQrcode.placeholder.customFee')" | |
| 69 | + style="width:100%" | |
| 70 | + > | |
| 71 | + <el-option | |
| 72 | + :label="$t('common.yes')" | |
| 73 | + value="ON" | |
| 74 | + /> | |
| 75 | + <el-option | |
| 76 | + :label="$t('common.no')" | |
| 77 | + value="OFF" | |
| 78 | + /> | |
| 79 | + </el-select> | |
| 80 | + </el-form-item> | |
| 81 | + | |
| 82 | + <el-form-item | |
| 83 | + :label="$t('payFeeQrcode.form.preFee')" | |
| 84 | + prop="preFee" | |
| 85 | + > | |
| 86 | + <el-select | |
| 87 | + v-model="form.preFee" | |
| 88 | + :placeholder="$t('payFeeQrcode.placeholder.preFee')" | |
| 89 | + style="width:100%" | |
| 90 | + > | |
| 91 | + <el-option | |
| 92 | + :label="$t('common.yes')" | |
| 93 | + value="ON" | |
| 94 | + /> | |
| 95 | + <el-option | |
| 96 | + :label="$t('common.no')" | |
| 97 | + value="OFF" | |
| 98 | + /> | |
| 99 | + </el-select> | |
| 100 | + </el-form-item> | |
| 101 | + | |
| 102 | + <el-form-item | |
| 103 | + :label="$t('payFeeQrcode.form.content')" | |
| 104 | + prop="content" | |
| 105 | + > | |
| 106 | + <el-input | |
| 107 | + v-model="form.content" | |
| 108 | + type="textarea" | |
| 109 | + :rows="3" | |
| 110 | + :placeholder="$t('payFeeQrcode.placeholder.content')" | |
| 111 | + /> | |
| 112 | + </el-form-item> | |
| 113 | + </el-form> | |
| 114 | + | |
| 115 | + <span slot="footer" class="dialog-footer"> | |
| 116 | + <el-button @click="visible = false"> | |
| 117 | + {{ $t('common.cancel') }} | |
| 118 | + </el-button> | |
| 119 | + <el-button | |
| 120 | + type="primary" | |
| 121 | + @click="handleSubmit" | |
| 122 | + > | |
| 123 | + {{ $t('common.confirm') }} | |
| 124 | + </el-button> | |
| 125 | + </span> | |
| 126 | + </el-dialog> | |
| 127 | +</template> | |
| 128 | + | |
| 129 | +<script> | |
| 130 | +import { savePayFeeQrcode } from '@/api/fee/payFeeQrcodeApi' | |
| 131 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 132 | + | |
| 133 | +export default { | |
| 134 | + name: 'AddPayFeeQrcode', | |
| 135 | + data() { | |
| 136 | + return { | |
| 137 | + visible: false, | |
| 138 | + form: { | |
| 139 | + qrcodeName: '', | |
| 140 | + queryWay: '', | |
| 141 | + smsValidate: 'OFF', | |
| 142 | + customFee: 'OFF', | |
| 143 | + preFee: 'OFF', | |
| 144 | + content: '', | |
| 145 | + state: 'ON', | |
| 146 | + feeType: 'OWNER', | |
| 147 | + communityId: '' | |
| 148 | + }, | |
| 149 | + rules: { | |
| 150 | + qrcodeName: [ | |
| 151 | + { required: true, message: this.$t('payFeeQrcode.rules.qrcodeName'), trigger: 'blur' }, | |
| 152 | + { max: 128, message: this.$t('payFeeQrcode.rules.qrcodeNameMax'), trigger: 'blur' } | |
| 153 | + ], | |
| 154 | + queryWay: [ | |
| 155 | + { required: true, message: this.$t('payFeeQrcode.rules.queryWay'), trigger: 'change' } | |
| 156 | + ], | |
| 157 | + smsValidate: [ | |
| 158 | + { required: true, message: this.$t('payFeeQrcode.rules.smsValidate'), trigger: 'change' } | |
| 159 | + ], | |
| 160 | + customFee: [ | |
| 161 | + { required: true, message: this.$t('payFeeQrcode.rules.customFee'), trigger: 'change' } | |
| 162 | + ], | |
| 163 | + preFee: [ | |
| 164 | + { required: true, message: this.$t('payFeeQrcode.rules.preFee'), trigger: 'change' } | |
| 165 | + ], | |
| 166 | + content: [ | |
| 167 | + { required: true, message: this.$t('payFeeQrcode.rules.content'), trigger: 'blur' } | |
| 168 | + ] | |
| 169 | + }, | |
| 170 | + queryWayOptions: [ | |
| 171 | + { value: '1001', label: this.$t('payFeeQrcode.queryWay.byPhone') }, | |
| 172 | + { value: '2002', label: this.$t('payFeeQrcode.queryWay.byHouse') }, | |
| 173 | + { value: '3003', label: this.$t('payFeeQrcode.queryWay.byBoth') } | |
| 174 | + ] | |
| 175 | + } | |
| 176 | + }, | |
| 177 | + methods: { | |
| 178 | + open() { | |
| 179 | + this.visible = true | |
| 180 | + this.form.communityId = getCommunityId() | |
| 181 | + this.$nextTick(() => { | |
| 182 | + this.$refs.form && this.$refs.form.resetFields() | |
| 183 | + }) | |
| 184 | + }, | |
| 185 | + handleClose() { | |
| 186 | + this.$refs.form.resetFields() | |
| 187 | + }, | |
| 188 | + handleSubmit() { | |
| 189 | + this.$refs.form.validate(async valid => { | |
| 190 | + if (valid) { | |
| 191 | + try { | |
| 192 | + await savePayFeeQrcode(this.form) | |
| 193 | + this.$message.success(this.$t('payFeeQrcode.message.addSuccess')) | |
| 194 | + this.visible = false | |
| 195 | + this.$emit('success') | |
| 196 | + } catch (error) { | |
| 197 | + this.$message.error(error.message || this.$t('payFeeQrcode.message.addFailed')) | |
| 198 | + } | |
| 199 | + } | |
| 200 | + }) | |
| 201 | + } | |
| 202 | + } | |
| 203 | +} | |
| 204 | +</script> | |
| 0 | 205 | \ No newline at end of file | ... | ... |
src/components/fee/deletePayFeeQrcode.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('payFeeQrcode.delete.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="30%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <div class="text-center"> | |
| 9 | + <i class="el-icon-warning" style="font-size: 60px; color: #E6A23C;"></i> | |
| 10 | + <p style="margin: 20px 0; font-size: 16px;"> | |
| 11 | + {{ $t('payFeeQrcode.delete.confirmText') }} | |
| 12 | + </p> | |
| 13 | + </div> | |
| 14 | + | |
| 15 | + <span slot="footer" class="dialog-footer"> | |
| 16 | + <el-button @click="visible = false"> | |
| 17 | + {{ $t('common.cancel') }} | |
| 18 | + </el-button> | |
| 19 | + <el-button | |
| 20 | + type="primary" | |
| 21 | + @click="handleConfirm" | |
| 22 | + > | |
| 23 | + {{ $t('common.confirm') }} | |
| 24 | + </el-button> | |
| 25 | + </span> | |
| 26 | + </el-dialog> | |
| 27 | +</template> | |
| 28 | + | |
| 29 | +<script> | |
| 30 | +import { deletePayFeeQrcode } from '@/api/fee/payFeeQrcodeApi' | |
| 31 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 32 | + | |
| 33 | +export default { | |
| 34 | + name: 'DeletePayFeeQrcode', | |
| 35 | + data() { | |
| 36 | + return { | |
| 37 | + visible: false, | |
| 38 | + currentData: { | |
| 39 | + pfqId: '', | |
| 40 | + communityId: '' | |
| 41 | + } | |
| 42 | + } | |
| 43 | + }, | |
| 44 | + methods: { | |
| 45 | + open(data) { | |
| 46 | + this.visible = true | |
| 47 | + this.currentData = { | |
| 48 | + pfqId: data.pfqId, | |
| 49 | + communityId: getCommunityId() | |
| 50 | + } | |
| 51 | + }, | |
| 52 | + handleClose() { | |
| 53 | + this.currentData = { | |
| 54 | + pfqId: '', | |
| 55 | + communityId: '' | |
| 56 | + } | |
| 57 | + }, | |
| 58 | + async handleConfirm() { | |
| 59 | + try { | |
| 60 | + await deletePayFeeQrcode(this.currentData) | |
| 61 | + this.$message.success(this.$t('payFeeQrcode.message.deleteSuccess')) | |
| 62 | + this.visible = false | |
| 63 | + this.$emit('success') | |
| 64 | + } catch (error) { | |
| 65 | + this.$message.error(error.message || this.$t('payFeeQrcode.message.deleteFailed')) | |
| 66 | + } | |
| 67 | + } | |
| 68 | + } | |
| 69 | +} | |
| 70 | +</script> | |
| 0 | 71 | \ No newline at end of file | ... | ... |
src/components/fee/editPayFeeQrcode.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('payFeeQrcode.edit.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="50%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <el-form | |
| 9 | + ref="form" | |
| 10 | + :model="form" | |
| 11 | + :rules="rules" | |
| 12 | + label-width="120px" | |
| 13 | + > | |
| 14 | + <el-form-item | |
| 15 | + :label="$t('payFeeQrcode.form.qrcodeName')" | |
| 16 | + prop="qrcodeName" | |
| 17 | + > | |
| 18 | + <el-input | |
| 19 | + v-model="form.qrcodeName" | |
| 20 | + :placeholder="$t('payFeeQrcode.placeholder.qrcodeName')" | |
| 21 | + /> | |
| 22 | + </el-form-item> | |
| 23 | + | |
| 24 | + <el-form-item | |
| 25 | + :label="$t('payFeeQrcode.form.queryWay')" | |
| 26 | + prop="queryWay" | |
| 27 | + > | |
| 28 | + <el-select | |
| 29 | + v-model="form.queryWay" | |
| 30 | + :placeholder="$t('payFeeQrcode.placeholder.queryWay')" | |
| 31 | + style="width:100%" | |
| 32 | + > | |
| 33 | + <el-option | |
| 34 | + v-for="item in queryWayOptions" | |
| 35 | + :key="item.value" | |
| 36 | + :label="item.label" | |
| 37 | + :value="item.value" | |
| 38 | + /> | |
| 39 | + </el-select> | |
| 40 | + </el-form-item> | |
| 41 | + | |
| 42 | + <el-form-item | |
| 43 | + :label="$t('payFeeQrcode.form.smsValidate')" | |
| 44 | + prop="smsValidate" | |
| 45 | + > | |
| 46 | + <el-select | |
| 47 | + v-model="form.smsValidate" | |
| 48 | + :placeholder="$t('payFeeQrcode.placeholder.smsValidate')" | |
| 49 | + style="width:100%" | |
| 50 | + > | |
| 51 | + <el-option | |
| 52 | + :label="$t('common.yes')" | |
| 53 | + value="ON" | |
| 54 | + /> | |
| 55 | + <el-option | |
| 56 | + :label="$t('common.no')" | |
| 57 | + value="OFF" | |
| 58 | + /> | |
| 59 | + </el-select> | |
| 60 | + </el-form-item> | |
| 61 | + | |
| 62 | + <el-form-item | |
| 63 | + :label="$t('payFeeQrcode.form.customFee')" | |
| 64 | + prop="customFee" | |
| 65 | + > | |
| 66 | + <el-select | |
| 67 | + v-model="form.customFee" | |
| 68 | + :placeholder="$t('payFeeQrcode.placeholder.customFee')" | |
| 69 | + style="width:100%" | |
| 70 | + > | |
| 71 | + <el-option | |
| 72 | + :label="$t('common.yes')" | |
| 73 | + value="ON" | |
| 74 | + /> | |
| 75 | + <el-option | |
| 76 | + :label="$t('common.no')" | |
| 77 | + value="OFF" | |
| 78 | + /> | |
| 79 | + </el-select> | |
| 80 | + </el-form-item> | |
| 81 | + | |
| 82 | + <el-form-item | |
| 83 | + :label="$t('payFeeQrcode.form.preFee')" | |
| 84 | + prop="preFee" | |
| 85 | + > | |
| 86 | + <el-select | |
| 87 | + v-model="form.preFee" | |
| 88 | + :placeholder="$t('payFeeQrcode.placeholder.preFee')" | |
| 89 | + style="width:100%" | |
| 90 | + > | |
| 91 | + <el-option | |
| 92 | + :label="$t('common.yes')" | |
| 93 | + value="ON" | |
| 94 | + /> | |
| 95 | + <el-option | |
| 96 | + :label="$t('common.no')" | |
| 97 | + value="OFF" | |
| 98 | + /> | |
| 99 | + </el-select> | |
| 100 | + </el-form-item> | |
| 101 | + | |
| 102 | + <el-form-item | |
| 103 | + :label="$t('payFeeQrcode.form.content')" | |
| 104 | + prop="content" | |
| 105 | + > | |
| 106 | + <el-input | |
| 107 | + v-model="form.content" | |
| 108 | + type="textarea" | |
| 109 | + :rows="3" | |
| 110 | + :placeholder="$t('payFeeQrcode.placeholder.content')" | |
| 111 | + /> | |
| 112 | + </el-form-item> | |
| 113 | + </el-form> | |
| 114 | + | |
| 115 | + <span slot="footer" class="dialog-footer"> | |
| 116 | + <el-button @click="visible = false"> | |
| 117 | + {{ $t('common.cancel') }} | |
| 118 | + </el-button> | |
| 119 | + <el-button | |
| 120 | + type="primary" | |
| 121 | + @click="handleSubmit" | |
| 122 | + > | |
| 123 | + {{ $t('common.confirm') }} | |
| 124 | + </el-button> | |
| 125 | + </span> | |
| 126 | + </el-dialog> | |
| 127 | +</template> | |
| 128 | + | |
| 129 | +<script> | |
| 130 | +import { updatePayFeeQrcode } from '@/api/fee/payFeeQrcodeApi' | |
| 131 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 132 | + | |
| 133 | +export default { | |
| 134 | + name: 'EditPayFeeQrcode', | |
| 135 | + data() { | |
| 136 | + return { | |
| 137 | + visible: false, | |
| 138 | + form: { | |
| 139 | + pfqId: '', | |
| 140 | + qrcodeName: '', | |
| 141 | + queryWay: '', | |
| 142 | + smsValidate: '', | |
| 143 | + customFee: '', | |
| 144 | + preFee: '', | |
| 145 | + content: '', | |
| 146 | + state: '', | |
| 147 | + feeType: '', | |
| 148 | + communityId: '' | |
| 149 | + }, | |
| 150 | + rules: { | |
| 151 | + qrcodeName: [ | |
| 152 | + { required: true, message: this.$t('payFeeQrcode.rules.qrcodeName'), trigger: 'blur' }, | |
| 153 | + { max: 128, message: this.$t('payFeeQrcode.rules.qrcodeNameMax'), trigger: 'blur' } | |
| 154 | + ], | |
| 155 | + queryWay: [ | |
| 156 | + { required: true, message: this.$t('payFeeQrcode.rules.queryWay'), trigger: 'change' } | |
| 157 | + ], | |
| 158 | + smsValidate: [ | |
| 159 | + { required: true, message: this.$t('payFeeQrcode.rules.smsValidate'), trigger: 'change' } | |
| 160 | + ], | |
| 161 | + customFee: [ | |
| 162 | + { required: true, message: this.$t('payFeeQrcode.rules.customFee'), trigger: 'change' } | |
| 163 | + ], | |
| 164 | + preFee: [ | |
| 165 | + { required: true, message: this.$t('payFeeQrcode.rules.preFee'), trigger: 'change' } | |
| 166 | + ], | |
| 167 | + content: [ | |
| 168 | + { required: true, message: this.$t('payFeeQrcode.rules.content'), trigger: 'blur' }, | |
| 169 | + { max: 512, message: this.$t('payFeeQrcode.rules.contentMax'), trigger: 'blur' } | |
| 170 | + ], | |
| 171 | + pfqId: [ | |
| 172 | + { required: true, message: this.$t('payFeeQrcode.rules.pfqId'), trigger: 'blur' } | |
| 173 | + ] | |
| 174 | + }, | |
| 175 | + queryWayOptions: [ | |
| 176 | + { value: '1001', label: this.$t('payFeeQrcode.queryWay.byPhone') }, | |
| 177 | + { value: '2002', label: this.$t('payFeeQrcode.queryWay.byHouse') }, | |
| 178 | + { value: '3003', label: this.$t('payFeeQrcode.queryWay.byBoth') } | |
| 179 | + ] | |
| 180 | + } | |
| 181 | + }, | |
| 182 | + methods: { | |
| 183 | + open(data) { | |
| 184 | + this.visible = true | |
| 185 | + this.form = { ...data } | |
| 186 | + this.form.communityId = getCommunityId() | |
| 187 | + this.$nextTick(() => { | |
| 188 | + this.$refs.form && this.$refs.form.clearValidate() | |
| 189 | + }) | |
| 190 | + }, | |
| 191 | + handleClose() { | |
| 192 | + this.$refs.form.resetFields() | |
| 193 | + }, | |
| 194 | + handleSubmit() { | |
| 195 | + this.$refs.form.validate(async valid => { | |
| 196 | + if (valid) { | |
| 197 | + try { | |
| 198 | + await updatePayFeeQrcode(this.form) | |
| 199 | + this.$message.success(this.$t('payFeeQrcode.message.editSuccess')) | |
| 200 | + this.visible = false | |
| 201 | + this.$emit('success') | |
| 202 | + } catch (error) { | |
| 203 | + this.$message.error(error.message || this.$t('payFeeQrcode.message.editFailed')) | |
| 204 | + } | |
| 205 | + } | |
| 206 | + }) | |
| 207 | + } | |
| 208 | + } | |
| 209 | +} | |
| 210 | +</script> | |
| 0 | 211 | \ No newline at end of file | ... | ... |
src/components/fee/viewPayFeeQrcode.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('payFeeQrcode.view.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="400px" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <div class="text-center"> | |
| 9 | + <div id="qrcode" style="width: 200px; height: 200px; margin: 0 auto;"></div> | |
| 10 | + <p style="margin: 15px 0; font-size: 16px; font-weight: bold;"> | |
| 11 | + {{ form.qrcodeName }} | |
| 12 | + </p> | |
| 13 | + <p style="color: #999;"> | |
| 14 | + {{ $t('payFeeQrcode.view.tip') }} | |
| 15 | + </p> | |
| 16 | + </div> | |
| 17 | + | |
| 18 | + <span slot="footer" class="dialog-footer"> | |
| 19 | + <el-button @click="visible = false"> | |
| 20 | + {{ $t('common.close') }} | |
| 21 | + </el-button> | |
| 22 | + </span> | |
| 23 | + </el-dialog> | |
| 24 | +</template> | |
| 25 | + | |
| 26 | +<script> | |
| 27 | +import QRCode from 'qrcodejs2' | |
| 28 | + | |
| 29 | +export default { | |
| 30 | + name: 'ViewPayFeeQrcode', | |
| 31 | + data() { | |
| 32 | + return { | |
| 33 | + visible: false, | |
| 34 | + form: { | |
| 35 | + qrcodeName: '', | |
| 36 | + qrCodeUrl: '' | |
| 37 | + }, | |
| 38 | + qrcode: null | |
| 39 | + } | |
| 40 | + }, | |
| 41 | + methods: { | |
| 42 | + open(data) { | |
| 43 | + this.visible = true | |
| 44 | + this.form = { ...data } | |
| 45 | + this.$nextTick(() => { | |
| 46 | + this.generateQRCode() | |
| 47 | + }) | |
| 48 | + }, | |
| 49 | + handleClose() { | |
| 50 | + if (this.qrcode) { | |
| 51 | + this.qrcode.clear() | |
| 52 | + document.getElementById('qrcode').innerHTML = '' | |
| 53 | + } | |
| 54 | + this.form = { | |
| 55 | + qrcodeName: '', | |
| 56 | + qrCodeUrl: '' | |
| 57 | + } | |
| 58 | + }, | |
| 59 | + generateQRCode() { | |
| 60 | + if (this.qrcode) { | |
| 61 | + this.qrcode.clear() | |
| 62 | + document.getElementById('qrcode').innerHTML = '' | |
| 63 | + } | |
| 64 | + | |
| 65 | + if (this.form.qrCodeUrl) { | |
| 66 | + this.qrcode = new QRCode(document.getElementById('qrcode'), { | |
| 67 | + text: this.form.qrCodeUrl, | |
| 68 | + width: 200, | |
| 69 | + height: 200, | |
| 70 | + colorDark: '#000000', | |
| 71 | + colorLight: '#ffffff', | |
| 72 | + correctLevel: QRCode.CorrectLevel.L | |
| 73 | + }) | |
| 74 | + } | |
| 75 | + } | |
| 76 | + }, | |
| 77 | + beforeDestroy() { | |
| 78 | + if (this.qrcode) { | |
| 79 | + this.qrcode.clear() | |
| 80 | + } | |
| 81 | + } | |
| 82 | +} | |
| 83 | +</script> | |
| 84 | + | |
| 85 | +<style scoped> | |
| 86 | +#qrcode { | |
| 87 | + display: flex; | |
| 88 | + justify-content: center; | |
| 89 | + align-items: center; | |
| 90 | + border: 1px solid #eee; | |
| 91 | + padding: 10px; | |
| 92 | +} | |
| 93 | +</style> | |
| 0 | 94 | \ No newline at end of file | ... | ... |
src/components/machine/cameraControlVideo.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="camera-control-video"> | |
| 3 | + <el-row> | |
| 4 | + <el-col :span="12"> | |
| 5 | + <h3>{{ $t('cameraControlVideo.camera') }}</h3> | |
| 6 | + </el-col> | |
| 7 | + <el-col :span="12" class="text-right"> | |
| 8 | + <el-button-group> | |
| 9 | + <el-button size="small" :type="cameraControlVideoInfo.cameraCount === 4 ? 'primary' : ''" | |
| 10 | + @click="_changeCount(4)"> | |
| 11 | + {{ $t('cameraControlVideo.fourWay') }} | |
| 12 | + </el-button> | |
| 13 | + <el-button size="small" :type="cameraControlVideoInfo.cameraCount === 6 ? 'primary' : ''" | |
| 14 | + @click="_changeCount(6)"> | |
| 15 | + {{ $t('cameraControlVideo.sixWay') }} | |
| 16 | + </el-button> | |
| 17 | + </el-button-group> | |
| 18 | + </el-col> | |
| 19 | + </el-row> | |
| 20 | + <el-row class="margin-top"> | |
| 21 | + <template v-if="cameraControlVideoInfo.cameraCount === 4"> | |
| 22 | + <el-col v-for="(item, index) in cameraControlVideoInfo.machines" :key="index" :span="12"> | |
| 23 | + <div class="form-group"> | |
| 24 | + <div :id="item.id" style="border: 1px solid #dee2e6;"> | |
| 25 | + <img width="100%" src="/img/init.jpg" height="300px" style="border: 1;" /> | |
| 26 | + </div> | |
| 27 | + <div class="flex justify-between margin-top-sm" style="font-size: 14px;"> | |
| 28 | + <div class="margin-left-sm"> | |
| 29 | + <i class="el-icon-bell" style="color: #007bff;"></i>{{ item.machineName }} | |
| 30 | + </div> | |
| 31 | + <el-button size="small" type="primary" @click="_openSelectVideo(item)"> | |
| 32 | + {{ $t('common.select') }} | |
| 33 | + </el-button> | |
| 34 | + </div> | |
| 35 | + </div> | |
| 36 | + </el-col> | |
| 37 | + </template> | |
| 38 | + <template v-else-if="cameraControlVideoInfo.cameraCount === 6"> | |
| 39 | + <el-col v-for="(item, index) in cameraControlVideoInfo.machines" :key="index" :span="8"> | |
| 40 | + <div class="form-group"> | |
| 41 | + <div :id="item.id" style="border: 1px solid #dee2e6;"> | |
| 42 | + <img width="100%" src="/img/init.jpg" height="300px" style="border: 1;" /> | |
| 43 | + </div> | |
| 44 | + <div class="flex justify-between margin-top-sm" style="font-size: 14px;"> | |
| 45 | + <div class="margin-left-sm"> | |
| 46 | + <i class="el-icon-bell" style="color: #007bff;"></i>{{ item.machineName }} | |
| 47 | + </div> | |
| 48 | + <el-button size="small" type="primary" @click="_openSelectVideo(item)"> | |
| 49 | + {{ $t('common.select') }} | |
| 50 | + </el-button> | |
| 51 | + </div> | |
| 52 | + </div> | |
| 53 | + </el-col> | |
| 54 | + </template> | |
| 55 | + </el-row> | |
| 56 | + <select-video-machine ref="selectVideoMachine" /> | |
| 57 | + </div> | |
| 58 | +</template> | |
| 59 | + | |
| 60 | +<script> | |
| 61 | +import SelectVideoMachine from './selectVideoMachine.vue' | |
| 62 | + | |
| 63 | +export default { | |
| 64 | + name: 'CameraControlVideo', | |
| 65 | + components: { | |
| 66 | + SelectVideoMachine | |
| 67 | + }, | |
| 68 | + data() { | |
| 69 | + return { | |
| 70 | + cameraControlVideoInfo: { | |
| 71 | + machines: [], | |
| 72 | + cameraCount: 4 | |
| 73 | + } | |
| 74 | + } | |
| 75 | + }, | |
| 76 | + created() { | |
| 77 | + const cameraCount = this.$route.query.cameraCount | |
| 78 | + if (cameraCount) { | |
| 79 | + this.cameraControlVideoInfo.cameraCount = parseInt(cameraCount) | |
| 80 | + } | |
| 81 | + this._initCamera() | |
| 82 | + }, | |
| 83 | + methods: { | |
| 84 | + _initCamera() { | |
| 85 | + const machines = [] | |
| 86 | + const cameraCount = this.cameraControlVideoInfo.cameraCount | |
| 87 | + for (let i = 0; i < cameraCount; i++) { | |
| 88 | + machines.push({ | |
| 89 | + id: 'cameraVideo' + (i + 1) + 'Div', | |
| 90 | + machineName: '', | |
| 91 | + url: '' | |
| 92 | + }) | |
| 93 | + } | |
| 94 | + this.cameraControlVideoInfo.machines = machines | |
| 95 | + }, | |
| 96 | + _openSelectVideo(item) { | |
| 97 | + item.callback = (machine) => { | |
| 98 | + item.machineName = machine.machineName | |
| 99 | + if (item.jessibuca) { | |
| 100 | + try { | |
| 101 | + item.jessibuca.destroy() | |
| 102 | + } catch (err) { | |
| 103 | + console.error(err) | |
| 104 | + } | |
| 105 | + } | |
| 106 | + this.$refs.selectVideoMachine.getPlayVideoUrl(machine.machineId) | |
| 107 | + .then(url => { | |
| 108 | + const image = document.getElementById(item.id) | |
| 109 | + const jessibuca = new window.Jessibuca({ | |
| 110 | + container: image, | |
| 111 | + videoBuffer: 0.2, | |
| 112 | + isResize: false, | |
| 113 | + text: "", | |
| 114 | + loadingText: "", | |
| 115 | + useMSE: false, | |
| 116 | + debug: false, | |
| 117 | + isNotMute: false, | |
| 118 | + supportDblclickFullscreen: true, | |
| 119 | + operateBtns: { | |
| 120 | + fullscreen: true, | |
| 121 | + screenshot: true, | |
| 122 | + play: true, | |
| 123 | + audio: false, | |
| 124 | + recorder: false | |
| 125 | + }, | |
| 126 | + }) | |
| 127 | + item.jessibuca = jessibuca | |
| 128 | + jessibuca.play(url) | |
| 129 | + }) | |
| 130 | + .catch(error => { | |
| 131 | + console.error(error) | |
| 132 | + this.$message.error(this.$t('cameraControlVideo.getVideoUrlError')) | |
| 133 | + }) | |
| 134 | + } | |
| 135 | + this.$refs.selectVideoMachine.open(item) | |
| 136 | + }, | |
| 137 | + _changeCount(count) { | |
| 138 | + this.cameraControlVideoInfo.cameraCount = count | |
| 139 | + this.$router.push({ | |
| 140 | + path: this.$route.path, | |
| 141 | + query: { cameraCount: count } | |
| 142 | + }) | |
| 143 | + this._initCamera() | |
| 144 | + } | |
| 145 | + } | |
| 146 | +} | |
| 147 | +</script> | |
| 148 | + | |
| 149 | +<style scoped> | |
| 150 | +.camera-control-video { | |
| 151 | + padding: 20px; | |
| 152 | + padding-top: 0; | |
| 153 | +} | |
| 154 | + | |
| 155 | +.text-right { | |
| 156 | + text-align: right; | |
| 157 | +} | |
| 158 | + | |
| 159 | +.margin-top { | |
| 160 | + margin-top: 20px; | |
| 161 | +} | |
| 162 | + | |
| 163 | +.margin-top-sm { | |
| 164 | + margin-top: 10px; | |
| 165 | +} | |
| 166 | + | |
| 167 | +.margin-left-sm { | |
| 168 | + margin-left: 10px; | |
| 169 | +} | |
| 170 | + | |
| 171 | +.flex { | |
| 172 | + display: flex; | |
| 173 | +} | |
| 174 | + | |
| 175 | +.justify-between { | |
| 176 | + justify-content: space-between; | |
| 177 | +} | |
| 178 | + | |
| 179 | +.form-group { | |
| 180 | + margin-bottom: 15px; | |
| 181 | +} | |
| 182 | +</style> | |
| 0 | 183 | \ No newline at end of file | ... | ... |
src/components/machine/selectVideoMachine.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :visible.sync="dialogVisible" | |
| 4 | + :title="$t('selectVideoMachine.title')" | |
| 5 | + width="80%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <el-row> | |
| 9 | + <el-col :span="12" class="border-right"> | |
| 10 | + <div class="text-center"> | |
| 11 | + {{ $t('selectVideoMachine.monitorArea') }} | |
| 12 | + </div> | |
| 13 | + <div class="padding padding-top-xs"> | |
| 14 | + <div | |
| 15 | + v-for="(item,index) in selectVideoMachineInfo.areas" | |
| 16 | + :key="index" | |
| 17 | + class="padding overflow-hidden" | |
| 18 | + :class="{'select': selectVideoMachineInfo.maId === item.maId}" | |
| 19 | + @click="_changeArea(item)" | |
| 20 | + > | |
| 21 | + <div>{{item.maName}}</div> | |
| 22 | + </div> | |
| 23 | + </div> | |
| 24 | + </el-col> | |
| 25 | + <el-col :span="12"> | |
| 26 | + <div class="text-center"> | |
| 27 | + {{ $t('selectVideoMachine.camera') }} | |
| 28 | + </div> | |
| 29 | + <div class="padding padding-top-xs"> | |
| 30 | + <div | |
| 31 | + v-for="(item,index) in selectVideoMachineInfo.machines" | |
| 32 | + :key="index" | |
| 33 | + class="padding overflow-hidden" | |
| 34 | + :class="{'select': selectVideoMachineInfo.machineId === item.machineId}" | |
| 35 | + @click="_changeMachine(item)" | |
| 36 | + > | |
| 37 | + <div>{{item.machineName}}</div> | |
| 38 | + </div> | |
| 39 | + </div> | |
| 40 | + </el-col> | |
| 41 | + </el-row> | |
| 42 | + </el-dialog> | |
| 43 | +</template> | |
| 44 | + | |
| 45 | +<script> | |
| 46 | +import { getMonitorAreas, getMonitorMachines, getPlayVideoUrl } from '@/api/machine/videoControlApi' | |
| 47 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 48 | + | |
| 49 | +export default { | |
| 50 | + name: 'SelectVideoMachine', | |
| 51 | + data() { | |
| 52 | + return { | |
| 53 | + dialogVisible: false, | |
| 54 | + selectVideoMachineInfo: { | |
| 55 | + areas: [], | |
| 56 | + machines: [], | |
| 57 | + maId: '', | |
| 58 | + machineId: '', | |
| 59 | + curMachine: {} | |
| 60 | + }, | |
| 61 | + communityId: '' | |
| 62 | + } | |
| 63 | + }, | |
| 64 | + created() { | |
| 65 | + this.communityId = getCommunityId() | |
| 66 | + }, | |
| 67 | + methods: { | |
| 68 | + open(machine) { | |
| 69 | + this._clearSelectVideoMachine() | |
| 70 | + this.selectVideoMachineInfo.curMachine = machine | |
| 71 | + this._loadSelectAreas() | |
| 72 | + this.dialogVisible = true | |
| 73 | + }, | |
| 74 | + async getPlayVideoUrl(machineId) { | |
| 75 | + const params = { | |
| 76 | + page: 1, | |
| 77 | + row: 1, | |
| 78 | + communityId: this.communityId, | |
| 79 | + machineId: machineId | |
| 80 | + } | |
| 81 | + const res = await getPlayVideoUrl(params) | |
| 82 | + return res.data | |
| 83 | + }, | |
| 84 | + async _loadSelectAreas() { | |
| 85 | + try { | |
| 86 | + const params = { | |
| 87 | + page: 1, | |
| 88 | + row: 100, | |
| 89 | + communityId: this.communityId | |
| 90 | + } | |
| 91 | + const res = await getMonitorAreas(params) | |
| 92 | + this.selectVideoMachineInfo.areas = res.data | |
| 93 | + if (res.data.length > 0) { | |
| 94 | + this._changeArea(res.data[0]) | |
| 95 | + } | |
| 96 | + } catch (error) { | |
| 97 | + this.$message.error(this.$t('selectVideoMachine.loadAreasError')) | |
| 98 | + } | |
| 99 | + }, | |
| 100 | + async _loadSelectVideoMachines() { | |
| 101 | + try { | |
| 102 | + const params = { | |
| 103 | + page: 1, | |
| 104 | + row: 100, | |
| 105 | + communityId: this.communityId, | |
| 106 | + maId: this.selectVideoMachineInfo.maId | |
| 107 | + } | |
| 108 | + const res = await getMonitorMachines(params) | |
| 109 | + this.selectVideoMachineInfo.machines = res.data | |
| 110 | + } catch (error) { | |
| 111 | + this.$message.error(this.$t('selectVideoMachine.loadMachinesError')) | |
| 112 | + } | |
| 113 | + }, | |
| 114 | + _changeArea(area) { | |
| 115 | + this.selectVideoMachineInfo.maId = area.maId | |
| 116 | + this._loadSelectVideoMachines() | |
| 117 | + }, | |
| 118 | + _changeMachine(machine) { | |
| 119 | + this.selectVideoMachineInfo.machineId = machine.machineId | |
| 120 | + this.selectVideoMachineInfo.curMachine.callback(machine) | |
| 121 | + this.handleClose() | |
| 122 | + }, | |
| 123 | + _clearSelectVideoMachine() { | |
| 124 | + this.selectVideoMachineInfo = { | |
| 125 | + areas: [], | |
| 126 | + machines: [], | |
| 127 | + maId: '', | |
| 128 | + machineId: '', | |
| 129 | + curMachine: {} | |
| 130 | + } | |
| 131 | + }, | |
| 132 | + handleClose() { | |
| 133 | + this.dialogVisible = false | |
| 134 | + this._clearSelectVideoMachine() | |
| 135 | + } | |
| 136 | + } | |
| 137 | +} | |
| 138 | +</script> | |
| 139 | + | |
| 140 | +<style scoped> | |
| 141 | +.border-right { | |
| 142 | + border-right: 1px solid #dee2e6; | |
| 143 | +} | |
| 144 | +.text-center { | |
| 145 | + text-align: center; | |
| 146 | + font-weight: bold; | |
| 147 | + margin-bottom: 10px; | |
| 148 | +} | |
| 149 | +.padding { | |
| 150 | + padding: 10px; | |
| 151 | +} | |
| 152 | +.padding-top-xs { | |
| 153 | + padding-top: 5px; | |
| 154 | +} | |
| 155 | +.overflow-hidden { | |
| 156 | + overflow: hidden; | |
| 157 | +} | |
| 158 | +.select { | |
| 159 | + background-color: #f5f7fa; | |
| 160 | + color: #409eff; | |
| 161 | + cursor: pointer; | |
| 162 | +} | |
| 163 | +</style> | |
| 0 | 164 | \ No newline at end of file | ... | ... |
src/components/room/addPropertyRightRegistration.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="$t('propertyRightRegistration.add.title')" :visible.sync="visible" width="70%" | |
| 3 | + @close="handleClose"> | |
| 4 | + <el-form ref="form" :model="formData" label-width="120px"> | |
| 5 | + <el-row :gutter="20"> | |
| 6 | + <el-col :span="12"> | |
| 7 | + <el-form-item :label="$t('propertyRightRegistration.add.floor')" prop="floorId" | |
| 8 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.floorRequired'), trigger: 'change' }]"> | |
| 9 | + <el-select v-model="formData.floorId" :placeholder="$t('propertyRightRegistration.add.floorPlaceholder')" | |
| 10 | + style="width:100%" @change="handleFloorChange"> | |
| 11 | + <el-option v-for="item in floors" :key="item.floorId" | |
| 12 | + :label="`${item.floorNum}${$t('propertyRightRegistration.add.floorUnit')}`" :value="item.floorId" /> | |
| 13 | + </el-select> | |
| 14 | + </el-form-item> | |
| 15 | + </el-col> | |
| 16 | + <el-col :span="12"> | |
| 17 | + <el-form-item :label="$t('propertyRightRegistration.add.unit')" prop="unitId" | |
| 18 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.unitRequired'), trigger: 'change' }]"> | |
| 19 | + <el-select v-model="formData.unitId" :placeholder="$t('propertyRightRegistration.add.unitPlaceholder')" | |
| 20 | + style="width:100%" @change="handleUnitChange"> | |
| 21 | + <el-option v-for="item in units" :key="item.unitId" | |
| 22 | + :label="`${item.unitNum}${$t('propertyRightRegistration.add.unitUnit')}`" :value="item.unitId" /> | |
| 23 | + </el-select> | |
| 24 | + </el-form-item> | |
| 25 | + </el-col> | |
| 26 | + </el-row> | |
| 27 | + | |
| 28 | + <el-row :gutter="20"> | |
| 29 | + <el-col :span="12"> | |
| 30 | + <el-form-item :label="$t('propertyRightRegistration.add.room')" prop="roomId" | |
| 31 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.roomRequired'), trigger: 'change' }]"> | |
| 32 | + <el-select v-model="formData.roomId" :placeholder="$t('propertyRightRegistration.add.roomPlaceholder')" | |
| 33 | + style="width:100%"> | |
| 34 | + <el-option v-for="item in rooms" :key="item.roomId" :label="item.roomNum" :value="item.roomId" /> | |
| 35 | + </el-select> | |
| 36 | + </el-form-item> | |
| 37 | + </el-col> | |
| 38 | + <el-col :span="12"> | |
| 39 | + <el-form-item :label="$t('propertyRightRegistration.add.name')" prop="name" :rules="[ | |
| 40 | + { required: true, message: $t('propertyRightRegistration.add.nameRequired'), trigger: 'blur' }, | |
| 41 | + { min: 2, max: 64, message: $t('propertyRightRegistration.add.nameLength'), trigger: 'blur' } | |
| 42 | + ]"> | |
| 43 | + <el-input v-model.trim="formData.name" :placeholder="$t('propertyRightRegistration.add.namePlaceholder')" /> | |
| 44 | + </el-form-item> | |
| 45 | + </el-col> | |
| 46 | + </el-row> | |
| 47 | + | |
| 48 | + <el-row :gutter="20"> | |
| 49 | + <el-col :span="12"> | |
| 50 | + <el-form-item :label="$t('propertyRightRegistration.add.link')" prop="link" | |
| 51 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.linkRequired'), trigger: 'blur' }]"> | |
| 52 | + <el-input v-model.trim="formData.link" :placeholder="$t('propertyRightRegistration.add.linkPlaceholder')" /> | |
| 53 | + </el-form-item> | |
| 54 | + </el-col> | |
| 55 | + <el-col :span="12"> | |
| 56 | + <el-form-item :label="$t('propertyRightRegistration.add.idCard')" prop="idCard" :rules="[ | |
| 57 | + { required: true, message: $t('propertyRightRegistration.add.idCardRequired'), trigger: 'blur' }, | |
| 58 | + ]"> | |
| 59 | + <el-input v-model.trim="formData.idCard" | |
| 60 | + :placeholder="$t('propertyRightRegistration.add.idCardPlaceholder')" /> | |
| 61 | + </el-form-item> | |
| 62 | + </el-col> | |
| 63 | + </el-row> | |
| 64 | + | |
| 65 | + <el-form-item :label="$t('propertyRightRegistration.add.address')" prop="address" :rules="[ | |
| 66 | + { required: true, message: $t('propertyRightRegistration.add.addressRequired'), trigger: 'blur' }, | |
| 67 | + { max: 255, message: $t('propertyRightRegistration.add.addressMax'), trigger: 'blur' } | |
| 68 | + ]"> | |
| 69 | + <el-input v-model.trim="formData.address" | |
| 70 | + :placeholder="$t('propertyRightRegistration.add.addressPlaceholder')" /> | |
| 71 | + </el-form-item> | |
| 72 | + | |
| 73 | + <el-form-item :label="$t('propertyRightRegistration.add.idCardPhotos')" prop="idCardPhotos" | |
| 74 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.idCardPhotosRequired'), trigger: 'change' }]"> | |
| 75 | + <upload-image-url ref="idCardUpload" :image-count="2" @notifyUploadCoverImage="handleIdCardChange" /> | |
| 76 | + <p class="tip">{{ $t('propertyRightRegistration.add.idCardPhotosTip') }}</p> | |
| 77 | + </el-form-item> | |
| 78 | + | |
| 79 | + <el-form-item :label="$t('propertyRightRegistration.add.housePurchasePhotos')" prop="housePurchasePhotos" | |
| 80 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.housePurchasePhotosRequired'), trigger: 'change' }]"> | |
| 81 | + <upload-image-url ref="housePurchaseUpload" :image-count="10" @notifyUploadCoverImage="handleHousePurchaseChange" /> | |
| 82 | + <p class="tip">{{ $t('propertyRightRegistration.add.housePurchasePhotosTip') }}</p> | |
| 83 | + </el-form-item> | |
| 84 | + | |
| 85 | + <el-row :gutter="20"> | |
| 86 | + <el-col :span="12"> | |
| 87 | + <el-form-item :label="$t('propertyRightRegistration.add.isTrue')" prop="isTrue" | |
| 88 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.isTrueRequired'), trigger: 'change' }]"> | |
| 89 | + <el-select v-model="formData.isTrue" :placeholder="$t('propertyRightRegistration.add.isTruePlaceholder')" | |
| 90 | + style="width:100%" @change="handleIsTrueChange"> | |
| 91 | + <el-option :label="$t('common.yes')" value="true" /> | |
| 92 | + <el-option :label="$t('common.no')" value="false" /> | |
| 93 | + </el-select> | |
| 94 | + </el-form-item> | |
| 95 | + </el-col> | |
| 96 | + <el-col v-if="formData.isTrue === 'true'" :span="12"> | |
| 97 | + <el-form-item :label="$t('propertyRightRegistration.add.repairPhotos')" prop="repairPhotos" | |
| 98 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.repairPhotosRequired'), trigger: 'change' }]"> | |
| 99 | + <upload-image-url ref="repairUpload" :image-count="3" @notifyUploadCoverImage="handleRepairChange" /> | |
| 100 | + </el-form-item> | |
| 101 | + </el-col> | |
| 102 | + </el-row> | |
| 103 | + | |
| 104 | + <el-row :gutter="20"> | |
| 105 | + <el-col :span="12"> | |
| 106 | + <el-form-item :label="$t('propertyRightRegistration.add.flag')" prop="flag" | |
| 107 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.flagRequired'), trigger: 'change' }]"> | |
| 108 | + <el-select v-model="formData.flag" :placeholder="$t('propertyRightRegistration.add.flagPlaceholder')" | |
| 109 | + style="width:100%" @change="handleFlagChange"> | |
| 110 | + <el-option :label="$t('common.yes')" value="0" /> | |
| 111 | + <el-option :label="$t('common.no')" value="1" /> | |
| 112 | + </el-select> | |
| 113 | + </el-form-item> | |
| 114 | + </el-col> | |
| 115 | + <el-col v-if="formData.flag === '0'" :span="12"> | |
| 116 | + <el-form-item :label="$t('propertyRightRegistration.add.deedTaxPhotos')" prop="deedTaxPhotos" | |
| 117 | + :rules="[{ required: true, message: $t('propertyRightRegistration.add.deedTaxPhotosRequired'), trigger: 'change' }]"> | |
| 118 | + <upload-image-url ref="deedTaxUpload" :image-count="3" @notifyUploadCoverImage="handleDeedTaxChange" /> | |
| 119 | + </el-form-item> | |
| 120 | + </el-col> | |
| 121 | + </el-row> | |
| 122 | + </el-form> | |
| 123 | + | |
| 124 | + <div slot="footer" class="dialog-footer"> | |
| 125 | + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button> | |
| 126 | + <el-button type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</el-button> | |
| 127 | + </div> | |
| 128 | + </el-dialog> | |
| 129 | +</template> | |
| 130 | + | |
| 131 | +<script> | |
| 132 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 133 | +import { getFloors, getUnits, queryRooms } from '@/api/room/roomApi' | |
| 134 | +import { savePropertyRightRegistration } from '@/api/room/propertyRightRegistrationManageApi' | |
| 135 | +import UploadImageUrl from '@/components/upload/UploadImageUrl' | |
| 136 | + | |
| 137 | +export default { | |
| 138 | + name: 'AddPropertyRightRegistration', | |
| 139 | + components: { | |
| 140 | + UploadImageUrl | |
| 141 | + }, | |
| 142 | + data() { | |
| 143 | + return { | |
| 144 | + visible: false, | |
| 145 | + formData: { | |
| 146 | + prrId: '', | |
| 147 | + roomId: '', | |
| 148 | + floorId: '', | |
| 149 | + unitId: '', | |
| 150 | + name: '', | |
| 151 | + link: '', | |
| 152 | + idCard: '', | |
| 153 | + address: '', | |
| 154 | + isTrue: '', | |
| 155 | + flag: '', | |
| 156 | + state: '0', | |
| 157 | + idCardPhotos: [], | |
| 158 | + housePurchasePhotos: [], | |
| 159 | + repairPhotos: [], | |
| 160 | + deedTaxPhotos: [], | |
| 161 | + communityId: getCommunityId() | |
| 162 | + }, | |
| 163 | + floors: [], | |
| 164 | + units: [], | |
| 165 | + rooms: [] | |
| 166 | + } | |
| 167 | + }, | |
| 168 | + methods: { | |
| 169 | + open() { | |
| 170 | + this.visible = true | |
| 171 | + this.getFloors() | |
| 172 | + this.$nextTick(() => { | |
| 173 | + this.$refs.form && this.$refs.form.resetFields() | |
| 174 | + this.$refs.idCardUpload && this.$refs.idCardUpload.clearImages() | |
| 175 | + this.$refs.housePurchaseUpload && this.$refs.housePurchaseUpload.clearImages() | |
| 176 | + this.$refs.repairUpload && this.$refs.repairUpload.clearImages() | |
| 177 | + this.$refs.deedTaxUpload && this.$refs.deedTaxUpload.clearImages() | |
| 178 | + }) | |
| 179 | + }, | |
| 180 | + handleClose() { | |
| 181 | + this.$refs.form.resetFields() | |
| 182 | + this.formData = { | |
| 183 | + prrId: '', | |
| 184 | + roomId: '', | |
| 185 | + floorId: '', | |
| 186 | + unitId: '', | |
| 187 | + name: '', | |
| 188 | + link: '', | |
| 189 | + idCard: '', | |
| 190 | + address: '', | |
| 191 | + isTrue: '', | |
| 192 | + flag: '', | |
| 193 | + state: '0', | |
| 194 | + idCardPhotos: [], | |
| 195 | + housePurchasePhotos: [], | |
| 196 | + repairPhotos: [], | |
| 197 | + deedTaxPhotos: [], | |
| 198 | + communityId: getCommunityId() | |
| 199 | + } | |
| 200 | + }, | |
| 201 | + async getFloors() { | |
| 202 | + try { | |
| 203 | + const params = { | |
| 204 | + communityId: this.formData.communityId, | |
| 205 | + page: 1, | |
| 206 | + row: 50 | |
| 207 | + } | |
| 208 | + const data = await getFloors(params) | |
| 209 | + this.floors = data.apiFloorDataVoList || [] | |
| 210 | + } catch (error) { | |
| 211 | + console.error('获取楼栋数据失败:', error) | |
| 212 | + } | |
| 213 | + }, | |
| 214 | + async handleFloorChange(floorId) { | |
| 215 | + try { | |
| 216 | + const params = { | |
| 217 | + floorId, | |
| 218 | + communityId: this.formData.communityId, | |
| 219 | + page: 1, | |
| 220 | + row: 50 | |
| 221 | + } | |
| 222 | + const data = await getUnits(params) | |
| 223 | + this.units = data || [] | |
| 224 | + this.formData.unitId = '' | |
| 225 | + this.rooms = [] | |
| 226 | + this.formData.roomId = '' | |
| 227 | + } catch (error) { | |
| 228 | + console.error('获取单元数据失败:', error) | |
| 229 | + } | |
| 230 | + }, | |
| 231 | + async handleUnitChange(unitId) { | |
| 232 | + try { | |
| 233 | + const params = { | |
| 234 | + unitId, | |
| 235 | + communityId: this.formData.communityId, | |
| 236 | + page: 1, | |
| 237 | + row: 50 | |
| 238 | + } | |
| 239 | + const data = await queryRooms(params) | |
| 240 | + this.rooms = data.rooms || [] | |
| 241 | + this.formData.roomId = '' | |
| 242 | + } catch (error) { | |
| 243 | + console.error('获取房间数据失败:', error) | |
| 244 | + } | |
| 245 | + }, | |
| 246 | + handleIsTrueChange(val) { | |
| 247 | + if (val !== 'true') { | |
| 248 | + this.formData.repairPhotos = [] | |
| 249 | + this.$refs.repairUpload && this.$refs.repairUpload.clearImages() | |
| 250 | + } | |
| 251 | + }, | |
| 252 | + handleFlagChange(val) { | |
| 253 | + if (val !== '0') { | |
| 254 | + this.formData.deedTaxPhotos = [] | |
| 255 | + this.$refs.deedTaxUpload && this.$refs.deedTaxUpload.clearImages() | |
| 256 | + } | |
| 257 | + }, | |
| 258 | + handleIdCardChange(photos) { | |
| 259 | + this.formData.idCardPhotos = photos | |
| 260 | + }, | |
| 261 | + handleHousePurchaseChange(photos) { | |
| 262 | + this.formData.housePurchasePhotos = photos | |
| 263 | + }, | |
| 264 | + handleRepairChange(photos) { | |
| 265 | + this.formData.repairPhotos = photos | |
| 266 | + }, | |
| 267 | + handleDeedTaxChange(photos) { | |
| 268 | + this.formData.deedTaxPhotos = photos | |
| 269 | + }, | |
| 270 | + | |
| 271 | + async handleSubmit() { | |
| 272 | + try { | |
| 273 | + await this.$refs.form.validate() | |
| 274 | + this.loading = true | |
| 275 | + | |
| 276 | + const params = { | |
| 277 | + ...this.formData, | |
| 278 | + idCardPhotos: this.formData.idCardPhotos, | |
| 279 | + housePurchasePhotos: this.formData.housePurchasePhotos, | |
| 280 | + repairPhotos: this.formData.repairPhotos, | |
| 281 | + deedTaxPhotos: this.formData.deedTaxPhotos | |
| 282 | + } | |
| 283 | + | |
| 284 | + await savePropertyRightRegistration(params) | |
| 285 | + this.$message.success(this.$t('propertyRightRegistration.add.success')) | |
| 286 | + this.visible = false | |
| 287 | + this.$emit('success') | |
| 288 | + } catch (error) { | |
| 289 | + console.error('保存失败:', error) | |
| 290 | + if (error !== 'validate') { | |
| 291 | + this.$message.error(this.$t('propertyRightRegistration.add.error')) | |
| 292 | + } | |
| 293 | + } finally { | |
| 294 | + this.loading = false | |
| 295 | + } | |
| 296 | + } | |
| 297 | + } | |
| 298 | +} | |
| 299 | +</script> | |
| 300 | + | |
| 301 | +<style scoped> | |
| 302 | +.tip { | |
| 303 | + color: #999; | |
| 304 | + font-size: 12px; | |
| 305 | + margin-top: 5px; | |
| 306 | +} | |
| 307 | +</style> | |
| 0 | 308 | \ No newline at end of file | ... | ... |
src/components/room/deletePropertyRightRegistration.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('propertyRightRegistration.delete.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="30%" | |
| 6 | + @close="visible = false" | |
| 7 | + > | |
| 8 | + <div class="delete-content"> | |
| 9 | + <p>{{ $t('propertyRightRegistration.delete.confirm') }}</p> | |
| 10 | + <p class="delete-tip">{{ $t('propertyRightRegistration.delete.tip') }}</p> | |
| 11 | + </div> | |
| 12 | + | |
| 13 | + <div slot="footer" class="dialog-footer"> | |
| 14 | + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button> | |
| 15 | + <el-button type="danger" @click="handleConfirm">{{ $t('common.confirm') }}</el-button> | |
| 16 | + </div> | |
| 17 | + </el-dialog> | |
| 18 | +</template> | |
| 19 | + | |
| 20 | +<script> | |
| 21 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 22 | +import { deletePropertyRightRegistration } from '@/api/room/propertyRightRegistrationManageApi' | |
| 23 | + | |
| 24 | +export default { | |
| 25 | + name: 'DeletePropertyRightRegistration', | |
| 26 | + data() { | |
| 27 | + return { | |
| 28 | + visible: false, | |
| 29 | + deleteData: { | |
| 30 | + prrId: '', | |
| 31 | + communityId: getCommunityId() | |
| 32 | + } | |
| 33 | + } | |
| 34 | + }, | |
| 35 | + methods: { | |
| 36 | + open(data) { | |
| 37 | + this.visible = true | |
| 38 | + this.deleteData = { | |
| 39 | + prrId: data.prrId, | |
| 40 | + communityId: getCommunityId() | |
| 41 | + } | |
| 42 | + }, | |
| 43 | + async handleConfirm() { | |
| 44 | + try { | |
| 45 | + const res = await deletePropertyRightRegistration(this.deleteData) | |
| 46 | + if (res.code === 0) { | |
| 47 | + this.$message.success(this.$t('propertyRightRegistration.delete.success')) | |
| 48 | + this.visible = false | |
| 49 | + this.$emit('success') | |
| 50 | + } else { | |
| 51 | + this.$message.error(res.msg || this.$t('propertyRightRegistration.delete.error')) | |
| 52 | + } | |
| 53 | + } catch (error) { | |
| 54 | + console.error('删除失败:', error) | |
| 55 | + this.$message.error(this.$t('propertyRightRegistration.delete.error')) | |
| 56 | + } | |
| 57 | + } | |
| 58 | + } | |
| 59 | +} | |
| 60 | +</script> | |
| 61 | + | |
| 62 | +<style scoped> | |
| 63 | +.delete-content { | |
| 64 | + text-align: center; | |
| 65 | + font-size: 16px; | |
| 66 | + padding: 20px 0; | |
| 67 | +} | |
| 68 | + | |
| 69 | +.delete-tip { | |
| 70 | + color: #f56c6c; | |
| 71 | + margin-top: 10px; | |
| 72 | + font-size: 14px; | |
| 73 | +} | |
| 74 | + | |
| 75 | +.dialog-footer { | |
| 76 | + text-align: right; | |
| 77 | +} | |
| 78 | +</style> | |
| 0 | 79 | \ No newline at end of file | ... | ... |
src/components/room/editPropertyRightRegistration.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="$t('propertyRightRegistration.edit.title')" :visible.sync="visible" width="70%" | |
| 3 | + @close="handleClose"> | |
| 4 | + <el-form ref="form" :model="formData" label-width="120px"> | |
| 5 | + <el-row :gutter="20"> | |
| 6 | + <el-col :span="12"> | |
| 7 | + <el-form-item :label="$t('propertyRightRegistration.edit.floor')" prop="floorId" | |
| 8 | + :rules="[{ required: true, message: $t('propertyRightRegistration.edit.floorRequired'), trigger: 'change' }]"> | |
| 9 | + <el-select v-model="formData.floorId" :placeholder="$t('propertyRightRegistration.edit.floorPlaceholder')" | |
| 10 | + style="width:100%" @change="handleFloorChange"> | |
| 11 | + <el-option v-for="item in floors" :key="item.floorId" | |
| 12 | + :label="`${item.floorNum}${$t('propertyRightRegistration.edit.floorUnit')}`" :value="item.floorId" /> | |
| 13 | + </el-select> | |
| 14 | + </el-form-item> | |
| 15 | + </el-col> | |
| 16 | + <el-col :span="12"> | |
| 17 | + <el-form-item :label="$t('propertyRightRegistration.edit.unit')" prop="unitId" | |
| 18 | + :rules="[{ required: true, message: $t('propertyRightRegistration.edit.unitRequired'), trigger: 'change' }]"> | |
| 19 | + <el-select v-model="formData.unitId" :placeholder="$t('propertyRightRegistration.edit.unitPlaceholder')" | |
| 20 | + style="width:100%" @change="handleUnitChange"> | |
| 21 | + <el-option v-for="item in units" :key="item.unitId" | |
| 22 | + :label="`${item.unitNum}${$t('propertyRightRegistration.edit.unitUnit')}`" :value="item.unitId" /> | |
| 23 | + </el-select> | |
| 24 | + </el-form-item> | |
| 25 | + </el-col> | |
| 26 | + </el-row> | |
| 27 | + | |
| 28 | + <el-row :gutter="20"> | |
| 29 | + <el-col :span="12"> | |
| 30 | + <el-form-item :label="$t('propertyRightRegistration.edit.room')" prop="roomId" | |
| 31 | + :rules="[{ required: true, message: $t('propertyRightRegistration.edit.roomRequired'), trigger: 'change' }]"> | |
| 32 | + <el-select v-model="formData.roomId" :placeholder="$t('propertyRightRegistration.edit.roomPlaceholder')" | |
| 33 | + style="width:100%"> | |
| 34 | + <el-option v-for="item in rooms" :key="item.roomId" :label="item.roomNum" :value="item.roomId" /> | |
| 35 | + </el-select> | |
| 36 | + </el-form-item> | |
| 37 | + </el-col> | |
| 38 | + <el-col :span="12"> | |
| 39 | + <el-form-item :label="$t('propertyRightRegistration.edit.name')" prop="name" :rules="[ | |
| 40 | + { required: true, message: $t('propertyRightRegistration.edit.nameRequired'), trigger: 'blur' }, | |
| 41 | + { min: 2, max: 64, message: $t('propertyRightRegistration.edit.nameLength'), trigger: 'blur' } | |
| 42 | + ]"> | |
| 43 | + <el-input v-model.trim="formData.name" | |
| 44 | + :placeholder="$t('propertyRightRegistration.edit.namePlaceholder')" /> | |
| 45 | + </el-form-item> | |
| 46 | + </el-col> | |
| 47 | + </el-row> | |
| 48 | + | |
| 49 | + <el-row :gutter="20"> | |
| 50 | + <el-col :span="12"> | |
| 51 | + <el-form-item :label="$t('propertyRightRegistration.edit.link')" prop="link" | |
| 52 | + :rules="[{ required: true, message: $t('propertyRightRegistration.edit.linkRequired'), trigger: 'blur' }]"> | |
| 53 | + <el-input v-model.trim="formData.link" | |
| 54 | + :placeholder="$t('propertyRightRegistration.edit.linkPlaceholder')" /> | |
| 55 | + </el-form-item> | |
| 56 | + </el-col> | |
| 57 | + <el-col :span="12"> | |
| 58 | + <el-form-item :label="$t('propertyRightRegistration.edit.idCard')" prop="idCard" :rules="[ | |
| 59 | + { required: true, message: $t('propertyRightRegistration.edit.idCardRequired'), trigger: 'blur' }, | |
| 60 | + ]"> | |
| 61 | + <el-input v-model.trim="formData.idCard" | |
| 62 | + :placeholder="$t('propertyRightRegistration.edit.idCardPlaceholder')" /> | |
| 63 | + </el-form-item> | |
| 64 | + </el-col> | |
| 65 | + </el-row> | |
| 66 | + | |
| 67 | + <el-form-item :label="$t('propertyRightRegistration.edit.address')" prop="address" :rules="[ | |
| 68 | + { required: true, message: $t('propertyRightRegistration.edit.addressRequired'), trigger: 'blur' }, | |
| 69 | + { max: 255, message: $t('propertyRightRegistration.edit.addressMax'), trigger: 'blur' } | |
| 70 | + ]"> | |
| 71 | + <el-input v-model.trim="formData.address" | |
| 72 | + :placeholder="$t('propertyRightRegistration.edit.addressPlaceholder')" /> | |
| 73 | + </el-form-item> | |
| 74 | + | |
| 75 | + <div class="dialog-footer"> | |
| 76 | + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button> | |
| 77 | + <el-button type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</el-button> | |
| 78 | + </div> | |
| 79 | + </el-form> | |
| 80 | + </el-dialog> | |
| 81 | +</template> | |
| 82 | + | |
| 83 | +<script> | |
| 84 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 85 | +import { getFloors, getUnits, queryRooms } from '@/api/room/roomApi' | |
| 86 | +import { updatePropertyRightRegistration } from '@/api/room/propertyRightRegistrationManageApi' | |
| 87 | + | |
| 88 | +export default { | |
| 89 | + name: 'EditPropertyRightRegistration', | |
| 90 | + data() { | |
| 91 | + return { | |
| 92 | + visible: false, | |
| 93 | + formData: { | |
| 94 | + prrId: '', | |
| 95 | + roomId: '', | |
| 96 | + floorId: '', | |
| 97 | + unitId: '', | |
| 98 | + name: '', | |
| 99 | + link: '', | |
| 100 | + idCard: '', | |
| 101 | + address: '', | |
| 102 | + communityId: getCommunityId() | |
| 103 | + }, | |
| 104 | + floors: [], | |
| 105 | + units: [], | |
| 106 | + rooms: [] | |
| 107 | + } | |
| 108 | + }, | |
| 109 | + methods: { | |
| 110 | + open(data) { | |
| 111 | + this.visible = true | |
| 112 | + this.formData = { | |
| 113 | + prrId: data.prrId, | |
| 114 | + roomId: data.roomId, | |
| 115 | + floorId: data.floorId, | |
| 116 | + unitId: data.unitId, | |
| 117 | + name: data.name, | |
| 118 | + link: data.link, | |
| 119 | + idCard: data.idCard, | |
| 120 | + address: data.address, | |
| 121 | + communityId: getCommunityId() | |
| 122 | + } | |
| 123 | + this.getFloors() | |
| 124 | + }, | |
| 125 | + handleClose() { | |
| 126 | + this.$refs.form.resetFields() | |
| 127 | + }, | |
| 128 | + async getFloors() { | |
| 129 | + try { | |
| 130 | + const params = { | |
| 131 | + communityId: this.formData.communityId, | |
| 132 | + page: 1, | |
| 133 | + row: 50 | |
| 134 | + } | |
| 135 | + const data = await getFloors(params) | |
| 136 | + this.floors = data.apiFloorDataVoList || [] | |
| 137 | + this.getUnits() | |
| 138 | + } catch (error) { | |
| 139 | + console.error('获取楼栋数据失败:', error) | |
| 140 | + } | |
| 141 | + }, | |
| 142 | + async getUnits() { | |
| 143 | + try { | |
| 144 | + const params = { | |
| 145 | + floorId: this.formData.floorId, | |
| 146 | + communityId: this.formData.communityId, | |
| 147 | + page: 1, | |
| 148 | + row: 50 | |
| 149 | + } | |
| 150 | + const data = await getUnits(params) | |
| 151 | + this.units = data || [] | |
| 152 | + this.getRooms() | |
| 153 | + } catch (error) { | |
| 154 | + console.error('获取单元数据失败:', error) | |
| 155 | + } | |
| 156 | + }, | |
| 157 | + async getRooms() { | |
| 158 | + try { | |
| 159 | + const params = { | |
| 160 | + unitId: this.formData.unitId, | |
| 161 | + communityId: this.formData.communityId, | |
| 162 | + page: 1, | |
| 163 | + row: 50 | |
| 164 | + } | |
| 165 | + const data = await queryRooms(params) | |
| 166 | + this.rooms = data.rooms || [] | |
| 167 | + } catch (error) { | |
| 168 | + console.error('获取房间数据失败:', error) | |
| 169 | + } | |
| 170 | + }, | |
| 171 | + handleFloorChange(floorId) { | |
| 172 | + this.formData.unitId = '' | |
| 173 | + this.formData.roomId = '' | |
| 174 | + this.units = [] | |
| 175 | + this.rooms = [] | |
| 176 | + if (floorId) { | |
| 177 | + this.getUnits() | |
| 178 | + } | |
| 179 | + }, | |
| 180 | + handleUnitChange(unitId) { | |
| 181 | + this.formData.roomId = '' | |
| 182 | + this.rooms = [] | |
| 183 | + if (unitId) { | |
| 184 | + this.getRooms() | |
| 185 | + } | |
| 186 | + }, | |
| 187 | + | |
| 188 | + async handleSubmit() { | |
| 189 | + try { | |
| 190 | + await this.$refs.form.validate() | |
| 191 | + | |
| 192 | + const params = { | |
| 193 | + ...this.formData, | |
| 194 | + flag: '0' // 0表示修改操作 | |
| 195 | + } | |
| 196 | + | |
| 197 | + const res = await updatePropertyRightRegistration(params) | |
| 198 | + if (res.code === 0) { | |
| 199 | + this.$message.success(this.$t('propertyRightRegistration.edit.success')) | |
| 200 | + this.visible = false | |
| 201 | + this.$emit('success') | |
| 202 | + } else { | |
| 203 | + this.$message.error(res.msg || this.$t('propertyRightRegistration.edit.error')) | |
| 204 | + } | |
| 205 | + } catch (error) { | |
| 206 | + if (error !== 'validate') { | |
| 207 | + console.error('修改失败:', error) | |
| 208 | + this.$message.error(this.$t('propertyRightRegistration.edit.error')) | |
| 209 | + } | |
| 210 | + } | |
| 211 | + } | |
| 212 | + } | |
| 213 | +} | |
| 214 | +</script> | |
| 215 | + | |
| 216 | +<style scoped> | |
| 217 | +.dialog-footer { | |
| 218 | + text-align: right; | |
| 219 | + margin-top: 20px; | |
| 220 | +} | |
| 221 | +</style> | |
| 0 | 222 | \ No newline at end of file | ... | ... |
src/components/room/editPropertyRightRegistrationDetail.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="$t('propertyRightDetail.edit.title')" :visible.sync="visible" width="70%" | |
| 3 | + :before-close="handleClose"> | |
| 4 | + <el-form ref="form" :model="editPropertyRightRegistrationDetailInfo" label-width="120px"> | |
| 5 | + <el-form-item :label="$t('propertyRightDetail.edit.materialType')"> | |
| 6 | + <el-input v-model="editPropertyRightRegistrationDetailInfo.securitiesName" | |
| 7 | + :placeholder="$t('propertyRightDetail.edit.materialTypePlaceholder')" disabled /> | |
| 8 | + </el-form-item> | |
| 9 | + | |
| 10 | + <template v-if="editPropertyRightRegistrationDetailInfo.securities === '001'"> | |
| 11 | + <el-form-item :label="$t('propertyRightDetail.edit.idCardPhoto')"> | |
| 12 | + <upload-image-url ref="idCardUpload" :image-count="2" @notifyUploadCoverImage="handleIdCardImageChange" /> | |
| 13 | + <p class="help-block">*{{ $t('propertyRightDetail.edit.idCardPhotoTip') }}*</p> | |
| 14 | + </el-form-item> | |
| 15 | + </template> | |
| 16 | + | |
| 17 | + <template v-if="editPropertyRightRegistrationDetailInfo.securities === '002'"> | |
| 18 | + <el-form-item :label="$t('propertyRightDetail.edit.houseContract')"> | |
| 19 | + <upload-image-url ref="housePurchaseUpload" :image-count="10" @notifyUploadCoverImage="handleHousePurchaseImageChange" /> | |
| 20 | + <p class="help-block">*{{ $t('propertyRightDetail.edit.houseContractTip') }}*</p> | |
| 21 | + </el-form-item> | |
| 22 | + </template> | |
| 23 | + | |
| 24 | + <template v-if="editPropertyRightRegistrationDetailInfo.securities === '003'"> | |
| 25 | + <el-form-item :label="$t('propertyRightDetail.edit.repairFund')"> | |
| 26 | + <el-select v-model="editPropertyRightRegistrationDetailInfo.isTrue" style="width: 100%" | |
| 27 | + :placeholder="$t('propertyRightDetail.edit.repairFundPlaceholder')"> | |
| 28 | + <el-option value="" disabled :label="$t('propertyRightDetail.edit.repairFundPlaceholder')" /> | |
| 29 | + <el-option :label="$t('common.yes')" value="true" /> | |
| 30 | + <el-option :label="$t('common.no')" value="false" /> | |
| 31 | + </el-select> | |
| 32 | + </el-form-item> | |
| 33 | + | |
| 34 | + <template v-if="editPropertyRightRegistrationDetailInfo.isTrue === 'true'"> | |
| 35 | + <el-form-item :label="$t('propertyRightDetail.edit.repairFundPhoto')"> | |
| 36 | + <upload-image-url ref="repairUpload" :image-count="3" @notifyUploadCoverImage="handleRepairImageChange" /> | |
| 37 | + </el-form-item> | |
| 38 | + </template> | |
| 39 | + </template> | |
| 40 | + | |
| 41 | + <template v-if="editPropertyRightRegistrationDetailInfo.securities === '004'"> | |
| 42 | + <el-form-item :label="$t('propertyRightDetail.edit.deedTax')"> | |
| 43 | + <el-select v-model="editPropertyRightRegistrationDetailInfo.isTrue" style="width: 100%" | |
| 44 | + :placeholder="$t('propertyRightDetail.edit.deedTaxPlaceholder')"> | |
| 45 | + <el-option value="" disabled :label="$t('propertyRightDetail.edit.deedTaxPlaceholder')" /> | |
| 46 | + <el-option :label="$t('common.yes')" value="true" /> | |
| 47 | + <el-option :label="$t('common.no')" value="false" /> | |
| 48 | + </el-select> | |
| 49 | + </el-form-item> | |
| 50 | + | |
| 51 | + <template v-if="editPropertyRightRegistrationDetailInfo.isTrue === 'true'"> | |
| 52 | + <el-form-item :label="$t('propertyRightDetail.edit.deedTaxPhoto')"> | |
| 53 | + <upload-image-url ref="deedTaxUpload" :image-count="3" @notifyUploadCoverImage="handleDeedTaxImageChange" /> | |
| 54 | + </el-form-item> | |
| 55 | + </template> | |
| 56 | + </template> | |
| 57 | + | |
| 58 | + <el-form-item class="dialog-footer"> | |
| 59 | + <el-button type="primary" @click="editPropertyRightRegistrationDetail"> | |
| 60 | + <i class="el-icon-check"></i> | |
| 61 | + {{ $t('common.save') }} | |
| 62 | + </el-button> | |
| 63 | + <el-button @click="handleClose"> | |
| 64 | + <i class="el-icon-close"></i> | |
| 65 | + {{ $t('common.cancel') }} | |
| 66 | + </el-button> | |
| 67 | + </el-form-item> | |
| 68 | + </el-form> | |
| 69 | + </el-dialog> | |
| 70 | +</template> | |
| 71 | + | |
| 72 | +<script> | |
| 73 | +import { updatePropertyRightRegistrationDetail } from '@/api/room/listPropertyRightRegistrationDetailApi' | |
| 74 | +import UploadImageUrl from '@/components/upload/UploadImageUrl' | |
| 75 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 76 | + | |
| 77 | +export default { | |
| 78 | + name: 'EditPropertyRightRegistrationDetail', | |
| 79 | + components: { | |
| 80 | + UploadImageUrl | |
| 81 | + }, | |
| 82 | + props: { | |
| 83 | + visible: { | |
| 84 | + type: Boolean, | |
| 85 | + default: false | |
| 86 | + } | |
| 87 | + }, | |
| 88 | + data() { | |
| 89 | + return { | |
| 90 | + editPropertyRightRegistrationDetailInfo: { | |
| 91 | + prrdId: '', | |
| 92 | + prrId: '', | |
| 93 | + securities: '', | |
| 94 | + securitiesName: '', | |
| 95 | + idCardUrl: '', | |
| 96 | + housePurchaseUrl: '', | |
| 97 | + repairUrl: '', | |
| 98 | + deedTaxUrl: '', | |
| 99 | + isTrue: '', | |
| 100 | + idCardPhotos: [], | |
| 101 | + housePurchasePhotos: [], | |
| 102 | + repairPhotos: [], | |
| 103 | + deedTaxPhotos: [], | |
| 104 | + communityId: '' | |
| 105 | + } | |
| 106 | + } | |
| 107 | + }, | |
| 108 | + methods: { | |
| 109 | + open(row) { | |
| 110 | + this.resetForm() | |
| 111 | + this.editPropertyRightRegistrationDetailInfo = { ...row } | |
| 112 | + this.editPropertyRightRegistrationDetailInfo.communityId = getCommunityId() | |
| 113 | + this.loadPhotos() | |
| 114 | + this.visible = true | |
| 115 | + }, | |
| 116 | + loadPhotos() { | |
| 117 | + if (this.editPropertyRightRegistrationDetailInfo.securities === '001' && this.editPropertyRightRegistrationDetailInfo.idCardUrl) { | |
| 118 | + const urls = this.editPropertyRightRegistrationDetailInfo.idCardUrl.trim() | |
| 119 | + if (urls) { | |
| 120 | + this.$refs.idCardUpload.setImages(urls.split(',')) | |
| 121 | + } | |
| 122 | + } | |
| 123 | + if (this.editPropertyRightRegistrationDetailInfo.securities === '002' && this.editPropertyRightRegistrationDetailInfo.housePurchaseUrl) { | |
| 124 | + const urls = this.editPropertyRightRegistrationDetailInfo.housePurchaseUrl.trim() | |
| 125 | + if (urls) { | |
| 126 | + this.$refs.housePurchaseUpload.setImages(urls.split(',')) | |
| 127 | + } | |
| 128 | + } | |
| 129 | + if (this.editPropertyRightRegistrationDetailInfo.securities === '003' && this.editPropertyRightRegistrationDetailInfo.repairUrl) { | |
| 130 | + const urls = this.editPropertyRightRegistrationDetailInfo.repairUrl.trim() | |
| 131 | + if (urls) { | |
| 132 | + this.$refs.repairUpload.setImages(urls.split(',')) | |
| 133 | + } | |
| 134 | + } | |
| 135 | + if (this.editPropertyRightRegistrationDetailInfo.securities === '004' && this.editPropertyRightRegistrationDetailInfo.deedTaxUrl) { | |
| 136 | + const urls = this.editPropertyRightRegistrationDetailInfo.deedTaxUrl.trim() | |
| 137 | + if (urls) { | |
| 138 | + this.$refs.deedTaxUpload.setImages(urls.split(',')) | |
| 139 | + } | |
| 140 | + } | |
| 141 | + }, | |
| 142 | + handleIdCardImageChange(photos) { | |
| 143 | + this.editPropertyRightRegistrationDetailInfo.idCardPhotos = photos | |
| 144 | + }, | |
| 145 | + handleHousePurchaseImageChange(photos) { | |
| 146 | + this.editPropertyRightRegistrationDetailInfo.housePurchasePhotos = photos | |
| 147 | + }, | |
| 148 | + handleRepairImageChange(photos) { | |
| 149 | + this.editPropertyRightRegistrationDetailInfo.repairPhotos = photos | |
| 150 | + }, | |
| 151 | + handleDeedTaxImageChange(photos) { | |
| 152 | + this.editPropertyRightRegistrationDetailInfo.deedTaxPhotos = photos | |
| 153 | + }, | |
| 154 | + editPropertyRightRegistrationDetail() { | |
| 155 | + if (!this.validateForm()) { | |
| 156 | + return | |
| 157 | + } | |
| 158 | + | |
| 159 | + const params = { ...this.editPropertyRightRegistrationDetailInfo } | |
| 160 | + if (params.securities === '001') { | |
| 161 | + params.idCardUrl = params.idCardPhotos && params.idCardPhotos.length > 0 ? params.idCardPhotos.join(',') : '' | |
| 162 | + } else if (params.securities === '002') { | |
| 163 | + params.housePurchaseUrl = params.housePurchasePhotos && params.housePurchasePhotos.length > 0 ? params.housePurchasePhotos.join(',') : '' | |
| 164 | + } else if (params.securities === '003') { | |
| 165 | + params.repairUrl = params.repairPhotos && params.repairPhotos.length > 0 ? params.repairPhotos.join(',') : '' | |
| 166 | + } else if (params.securities === '004') { | |
| 167 | + params.deedTaxUrl = params.deedTaxPhotos && params.deedTaxPhotos.length > 0 ? params.deedTaxPhotos.join(',') : '' | |
| 168 | + } | |
| 169 | + | |
| 170 | + updatePropertyRightRegistrationDetail(params) | |
| 171 | + .then(() => { | |
| 172 | + this.$message.success(this.$t('common.updateSuccess')) | |
| 173 | + this.$emit('success') | |
| 174 | + this.handleClose() | |
| 175 | + }) | |
| 176 | + .catch(error => { | |
| 177 | + this.$message.error(error.message || this.$t('common.updateFailed')) | |
| 178 | + }) | |
| 179 | + }, | |
| 180 | + validateForm() { | |
| 181 | + if (!this.editPropertyRightRegistrationDetailInfo.securities) { | |
| 182 | + this.$message.error(this.$t('propertyRightDetail.edit.materialTypeRequired')) | |
| 183 | + return false | |
| 184 | + } | |
| 185 | + return true | |
| 186 | + }, | |
| 187 | + handleClose() { | |
| 188 | + this.visible = false | |
| 189 | + }, | |
| 190 | + resetForm() { | |
| 191 | + this.editPropertyRightRegistrationDetailInfo = { | |
| 192 | + prrdId: '', | |
| 193 | + prrId: '', | |
| 194 | + securities: '', | |
| 195 | + securitiesName: '', | |
| 196 | + idCardUrl: '', | |
| 197 | + housePurchaseUrl: '', | |
| 198 | + repairUrl: '', | |
| 199 | + deedTaxUrl: '', | |
| 200 | + isTrue: '', | |
| 201 | + idCardPhotos: [], | |
| 202 | + housePurchasePhotos: [], | |
| 203 | + repairPhotos: [], | |
| 204 | + deedTaxPhotos: [], | |
| 205 | + communityId: '' | |
| 206 | + } | |
| 207 | + } | |
| 208 | + } | |
| 209 | +} | |
| 210 | +</script> | |
| 211 | + | |
| 212 | +<style lang="scss" scoped> | |
| 213 | +.dialog-footer { | |
| 214 | + text-align: right; | |
| 215 | + margin-top: 20px; | |
| 216 | +} | |
| 217 | + | |
| 218 | +.help-block { | |
| 219 | + color: #f56c6c; | |
| 220 | + font-size: 12px; | |
| 221 | + margin-top: 5px; | |
| 222 | +} | |
| 223 | +</style> | |
| 0 | 224 | \ No newline at end of file | ... | ... |
src/components/room/examinePropertyRightRegistration.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="$t('propertyRightRegistration.examine.title')" :visible.sync="visible" width="50%" | |
| 3 | + @close="handleClose"> | |
| 4 | + <el-form ref="form" :model="formData" label-width="120px"> | |
| 5 | + <el-form-item :label="$t('propertyRightRegistration.examine.room')"> | |
| 6 | + <el-input v-model="formData.allNum" disabled | |
| 7 | + :placeholder="$t('propertyRightRegistration.examine.roomPlaceholder')" /> | |
| 8 | + </el-form-item> | |
| 9 | + | |
| 10 | + <el-form-item :label="$t('propertyRightRegistration.examine.state')" prop="state" | |
| 11 | + :rules="[{ required: true, message: $t('propertyRightRegistration.examine.stateRequired'), trigger: 'change' }]"> | |
| 12 | + <el-select v-model="formData.state" :placeholder="$t('propertyRightRegistration.examine.statePlaceholder')" | |
| 13 | + style="width:100%"> | |
| 14 | + <el-option v-for="item in stateOptions" :key="item.statusCd" :label="item.name" :value="item.statusCd" | |
| 15 | + :disabled="item.statusCd === '0'" /> | |
| 16 | + </el-select> | |
| 17 | + </el-form-item> | |
| 18 | + | |
| 19 | + <el-form-item :label="$t('propertyRightRegistration.examine.remark')"> | |
| 20 | + <el-input v-model="formData.remark" type="textarea" :rows="3" | |
| 21 | + :placeholder="$t('propertyRightRegistration.examine.remarkPlaceholder')" /> | |
| 22 | + </el-form-item> | |
| 23 | + </el-form> | |
| 24 | + | |
| 25 | + <div slot="footer" class="dialog-footer"> | |
| 26 | + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button> | |
| 27 | + <el-button type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</el-button> | |
| 28 | + </div> | |
| 29 | + </el-dialog> | |
| 30 | +</template> | |
| 31 | + | |
| 32 | +<script> | |
| 33 | +import { getDict ,getCommunityId} from '@/api/community/communityApi' | |
| 34 | +import { updatePropertyRightRegistration } from '@/api/room/propertyRightRegistrationManageApi' | |
| 35 | + | |
| 36 | +export default { | |
| 37 | + name: 'ExaminePropertyRightRegistration', | |
| 38 | + data() { | |
| 39 | + return { | |
| 40 | + visible: false, | |
| 41 | + formData: { | |
| 42 | + prrId: '', | |
| 43 | + roomId: '', | |
| 44 | + floorNum: '', | |
| 45 | + unitNum: '', | |
| 46 | + roomNum: '', | |
| 47 | + allNum: '', | |
| 48 | + state: '', | |
| 49 | + remark: '', | |
| 50 | + flag: '1' | |
| 51 | + }, | |
| 52 | + stateOptions: [] | |
| 53 | + } | |
| 54 | + }, | |
| 55 | + methods: { | |
| 56 | + async open(data) { | |
| 57 | + this.visible = true | |
| 58 | + await this.getStateOptions() | |
| 59 | + | |
| 60 | + this.formData = { | |
| 61 | + prrId: data.prrId, | |
| 62 | + roomId: data.roomId, | |
| 63 | + floorNum: data.floorNum, | |
| 64 | + unitNum: data.unitNum, | |
| 65 | + roomNum: data.roomNum, | |
| 66 | + allNum: `${data.floorNum}-${data.unitNum}-${data.roomNum}`, | |
| 67 | + state: data.state === '0' ? '' : data.state, | |
| 68 | + remark: data.remark || '', | |
| 69 | + flag: '1' | |
| 70 | + } | |
| 71 | + }, | |
| 72 | + async getStateOptions() { | |
| 73 | + try { | |
| 74 | + this.stateOptions = await getDict('property_right_registration', 'state') | |
| 75 | + } catch (error) { | |
| 76 | + console.error('获取审核状态失败:', error) | |
| 77 | + this.$message.error(this.$t('propertyRightRegistration.examine.fetchStateError')) | |
| 78 | + } | |
| 79 | + }, | |
| 80 | + handleClose() { | |
| 81 | + this.$refs.form.resetFields() | |
| 82 | + this.formData = { | |
| 83 | + prrId: '', | |
| 84 | + roomId: '', | |
| 85 | + floorNum: '', | |
| 86 | + unitNum: '', | |
| 87 | + roomNum: '', | |
| 88 | + allNum: '', | |
| 89 | + state: '', | |
| 90 | + remark: '', | |
| 91 | + flag: '1' | |
| 92 | + } | |
| 93 | + }, | |
| 94 | + async handleSubmit() { | |
| 95 | + try { | |
| 96 | + await this.$refs.form.validate() | |
| 97 | + | |
| 98 | + const params = { | |
| 99 | + ...this.formData, | |
| 100 | + communityId: getCommunityId() | |
| 101 | + } | |
| 102 | + | |
| 103 | + const res = await updatePropertyRightRegistration(params) | |
| 104 | + if (res.code === 0) { | |
| 105 | + this.$message.success(this.$t('propertyRightRegistration.examine.success')) | |
| 106 | + this.visible = false | |
| 107 | + this.$emit('success') | |
| 108 | + } else { | |
| 109 | + this.$message.error(res.msg || this.$t('propertyRightRegistration.examine.error')) | |
| 110 | + } | |
| 111 | + } catch (error) { | |
| 112 | + if (error !== 'validate') { | |
| 113 | + console.error('审核失败:', error) | |
| 114 | + this.$message.error(this.$t('propertyRightRegistration.examine.error')) | |
| 115 | + } | |
| 116 | + } | |
| 117 | + } | |
| 118 | + } | |
| 119 | +} | |
| 120 | +</script> | |
| 121 | + | |
| 122 | +<style scoped> | |
| 123 | +.dialog-footer { | |
| 124 | + text-align: right; | |
| 125 | +} | |
| 126 | +</style> | |
| 0 | 127 | \ No newline at end of file | ... | ... |
src/components/room/floorUnitAllTree.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card class="tree-card"> | |
| 3 | + <el-tree | |
| 4 | + ref="tree" | |
| 5 | + :data="treeData" | |
| 6 | + :props="defaultProps" | |
| 7 | + node-key="id" | |
| 8 | + :default-expanded-keys="defaultExpandedKeys" | |
| 9 | + :highlight-current="true" | |
| 10 | + @node-click="handleNodeClick" | |
| 11 | + > | |
| 12 | + <template #default="{ node, data }"> | |
| 13 | + <span class="custom-tree-node"> | |
| 14 | + <img v-if="data.icon" :src="data.icon" class="tree-icon" /> | |
| 15 | + <span>{{ node.label }}</span> | |
| 16 | + </span> | |
| 17 | + </template> | |
| 18 | + </el-tree> | |
| 19 | + </el-card> | |
| 20 | +</template> | |
| 21 | + | |
| 22 | +<script> | |
| 23 | +import { queryFloorAndUnits } from '@/api/room/roomStructureApi' | |
| 24 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 25 | + | |
| 26 | +export default { | |
| 27 | + name: 'FloorUnitAllTree', | |
| 28 | + props: { | |
| 29 | + callBackListener: { | |
| 30 | + type: String, | |
| 31 | + default: '' | |
| 32 | + } | |
| 33 | + }, | |
| 34 | + data() { | |
| 35 | + return { | |
| 36 | + floorUnitTreeInfo: { | |
| 37 | + units: [], | |
| 38 | + floorId: '' | |
| 39 | + }, | |
| 40 | + treeData: [], | |
| 41 | + defaultProps: { | |
| 42 | + children: 'children', | |
| 43 | + label: 'text' | |
| 44 | + }, | |
| 45 | + defaultExpandedKeys: [], | |
| 46 | + communityId: '' | |
| 47 | + } | |
| 48 | + }, | |
| 49 | + created() { | |
| 50 | + this.communityId = getCommunityId() | |
| 51 | + this.loadFloorAndUnits() | |
| 52 | + }, | |
| 53 | + methods: { | |
| 54 | + async loadFloorAndUnits() { | |
| 55 | + try { | |
| 56 | + const params = { | |
| 57 | + communityId: this.communityId | |
| 58 | + } | |
| 59 | + const data = await queryFloorAndUnits(params) | |
| 60 | + this.floorUnitTreeInfo.units = data | |
| 61 | + this.initTreeData() | |
| 62 | + } catch (error) { | |
| 63 | + console.error('Failed to load floor and units:', error) | |
| 64 | + } | |
| 65 | + }, | |
| 66 | + initTreeData() { | |
| 67 | + this.treeData = this.doTreeData() | |
| 68 | + if (this.treeData.length > 0 && this.treeData[0].children) { | |
| 69 | + this.defaultExpandedKeys = [this.treeData[0].id] | |
| 70 | + } | |
| 71 | + }, | |
| 72 | + doTreeData() { | |
| 73 | + const treeData = [] | |
| 74 | + const units = this.floorUnitTreeInfo.units | |
| 75 | + | |
| 76 | + // Build first level menu (floors) | |
| 77 | + units.forEach(item => { | |
| 78 | + const existingFloor = treeData.find(floor => floor.floorId === item.floorId) | |
| 79 | + if (!existingFloor) { | |
| 80 | + const floorItem = { | |
| 81 | + id: `f_${item.floorId}`, | |
| 82 | + floorId: item.floorId, | |
| 83 | + floorNum: item.floorNum, | |
| 84 | + icon: "/img/floor.png", | |
| 85 | + text: `${item.floorNum}${this.$t('floorUnitTree.building')}(${item.floorName})`, | |
| 86 | + children: this.doTreeChildrenData(item.floorId) | |
| 87 | + } | |
| 88 | + treeData.push(floorItem) | |
| 89 | + } | |
| 90 | + }) | |
| 91 | + | |
| 92 | + return treeData | |
| 93 | + }, | |
| 94 | + doTreeChildrenData(floorId) { | |
| 95 | + const children = [] | |
| 96 | + const units = this.floorUnitTreeInfo.units | |
| 97 | + | |
| 98 | + units.forEach(item => { | |
| 99 | + if (item.floorId === floorId && item.unitId) { | |
| 100 | + children.push({ | |
| 101 | + id: `u_${item.unitId}`, | |
| 102 | + unitId: item.unitId, | |
| 103 | + text: `${item.unitNum}${this.$t('floorUnitTree.unit')}`, | |
| 104 | + icon: "/img/unit.png" | |
| 105 | + }) | |
| 106 | + } | |
| 107 | + }) | |
| 108 | + | |
| 109 | + return children | |
| 110 | + }, | |
| 111 | + handleNodeClick(data) { | |
| 112 | + if (data.id.startsWith('f_')) { | |
| 113 | + this.$emit('switchFloor', { | |
| 114 | + floorId: data.floorId | |
| 115 | + }) | |
| 116 | + } else { | |
| 117 | + this.$emit('switchUnit', { | |
| 118 | + unitId: data.unitId | |
| 119 | + }) | |
| 120 | + } | |
| 121 | + }, | |
| 122 | + refreshTree(params) { | |
| 123 | + if (params) { | |
| 124 | + this.floorUnitTreeInfo.floorId = params.floorId | |
| 125 | + } | |
| 126 | + this.loadFloorAndUnits() | |
| 127 | + } | |
| 128 | + } | |
| 129 | +} | |
| 130 | +</script> | |
| 131 | + | |
| 132 | +<style lang="scss" scoped> | |
| 133 | +.tree-card { | |
| 134 | + height: 100%; | |
| 135 | + | |
| 136 | + .custom-tree-node { | |
| 137 | + display: flex; | |
| 138 | + align-items: center; | |
| 139 | + | |
| 140 | + .tree-icon { | |
| 141 | + width: 16px; | |
| 142 | + height: 16px; | |
| 143 | + margin-right: 5px; | |
| 144 | + } | |
| 145 | + } | |
| 146 | + | |
| 147 | + :deep(.el-tree-node__content) { | |
| 148 | + height: 36px; | |
| 149 | + } | |
| 150 | +} | |
| 151 | +</style> | |
| 0 | 152 | \ No newline at end of file | ... | ... |
src/components/room/uploadImageUrl.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="upload-image-wrapper"> | |
| 3 | + <div v-for="(image, index) in photos" :key="index" class="image-preview"> | |
| 4 | + <el-image | |
| 5 | + :src="getImageUrl(image)" | |
| 6 | + fit="cover" | |
| 7 | + class="preview-image" | |
| 8 | + :preview-src-list="previewList" | |
| 9 | + > | |
| 10 | + <div slot="error" class="image-error"> | |
| 11 | + <img src="/img/noPhoto.jpg" class="error-image"> | |
| 12 | + </div> | |
| 13 | + </el-image> | |
| 14 | + <i class="el-icon-delete delete-icon" @click="removeImage(index)"></i> | |
| 15 | + </div> | |
| 16 | + | |
| 17 | + <div | |
| 18 | + v-if="photos.length < imageCount" | |
| 19 | + class="upload-button" | |
| 20 | + @click="triggerUpload" | |
| 21 | + > | |
| 22 | + <i class="el-icon-plus"></i> | |
| 23 | + </div> | |
| 24 | + | |
| 25 | + <input | |
| 26 | + type="file" | |
| 27 | + ref="fileInput" | |
| 28 | + accept="image/*" | |
| 29 | + class="file-input" | |
| 30 | + @change="handleFileChange" | |
| 31 | + /> | |
| 32 | + </div> | |
| 33 | +</template> | |
| 34 | + | |
| 35 | +<script> | |
| 36 | +import { uploadFile } from '@/api/common' | |
| 37 | + | |
| 38 | +export default { | |
| 39 | + name: 'UploadImageUrl', | |
| 40 | + props: { | |
| 41 | + imageCount: { | |
| 42 | + type: Number, | |
| 43 | + default: 99 | |
| 44 | + } | |
| 45 | + }, | |
| 46 | + data() { | |
| 47 | + return { | |
| 48 | + photos: [], // 用于显示的图片数组 | |
| 49 | + photosUrl: [] // 包含fileId和url的对象数组 | |
| 50 | + } | |
| 51 | + }, | |
| 52 | + computed: { | |
| 53 | + previewList() { | |
| 54 | + return this.photos.map(item => this.getImageUrl(item)) | |
| 55 | + } | |
| 56 | + }, | |
| 57 | + methods: { | |
| 58 | + getImageUrl(item) { | |
| 59 | + if (typeof item === 'string') { | |
| 60 | + if (item.startsWith('http') || item.startsWith('https') || item.startsWith('data:')) { | |
| 61 | + return item | |
| 62 | + } | |
| 63 | + return `/callComponent/download/getFile/file?fileId=${item}&communityId=-1&time=${new Date()}` | |
| 64 | + } | |
| 65 | + return item.url || '' | |
| 66 | + }, | |
| 67 | + triggerUpload() { | |
| 68 | + this.$refs.fileInput.click() | |
| 69 | + }, | |
| 70 | + handleFileChange(event) { | |
| 71 | + const file = event.target.files[0] | |
| 72 | + if (!file) return | |
| 73 | + | |
| 74 | + if (file.size > 2 * 1024 * 1024) { | |
| 75 | + this.$message.error(this.$t('upload.imageSizeLimit')) | |
| 76 | + return | |
| 77 | + } | |
| 78 | + | |
| 79 | + const reader = new FileReader() | |
| 80 | + reader.onload = (e) => { | |
| 81 | + const base64Image = e.target.result | |
| 82 | + this.photos.push(base64Image) | |
| 83 | + this.uploadFile(file) | |
| 84 | + } | |
| 85 | + reader.readAsDataURL(file) | |
| 86 | + event.target.value = null | |
| 87 | + }, | |
| 88 | + uploadFile(file) { | |
| 89 | + const formData = new FormData() | |
| 90 | + formData.append('uploadFile', file) | |
| 91 | + formData.append('communityId', this.$store.getters.communityId) | |
| 92 | + | |
| 93 | + uploadFile(formData) | |
| 94 | + .then(response => { | |
| 95 | + const newPhoto = { | |
| 96 | + fileId: response.data.fileId, | |
| 97 | + url: response.data.url | |
| 98 | + } | |
| 99 | + this.photosUrl.push(newPhoto) | |
| 100 | + this.$emit('change', this.photosUrl) | |
| 101 | + }) | |
| 102 | + .catch(error => { | |
| 103 | + this.$message.error(error.message || this.$t('upload.failed')) | |
| 104 | + this.photos.pop() | |
| 105 | + }) | |
| 106 | + }, | |
| 107 | + removeImage(index) { | |
| 108 | + this.photos.splice(index, 1) | |
| 109 | + this.photosUrl.splice(index, 1) | |
| 110 | + this.$emit('change', this.photosUrl) | |
| 111 | + }, | |
| 112 | + setPhotos(photos) { | |
| 113 | + this.photos = [...photos] | |
| 114 | + this.photosUrl = photos.map(photo => ({ | |
| 115 | + fileId: photo, | |
| 116 | + url: `/callComponent/download/getFile/file?fileId=${photo}&communityId=-1&time=${new Date()}` | |
| 117 | + })) | |
| 118 | + this.$emit('change', this.photosUrl) | |
| 119 | + }, | |
| 120 | + clear() { | |
| 121 | + this.photos = [] | |
| 122 | + this.photosUrl = [] | |
| 123 | + this.$emit('change', this.photosUrl) | |
| 124 | + } | |
| 125 | + } | |
| 126 | +} | |
| 127 | +</script> | |
| 128 | + | |
| 129 | +<style lang="scss" scoped> | |
| 130 | +.upload-image-wrapper { | |
| 131 | + display: flex; | |
| 132 | + flex-wrap: wrap; | |
| 133 | + gap: 10px; | |
| 134 | + | |
| 135 | + .image-preview { | |
| 136 | + position: relative; | |
| 137 | + width: 100px; | |
| 138 | + height: 100px; | |
| 139 | + | |
| 140 | + .preview-image { | |
| 141 | + width: 100%; | |
| 142 | + height: 100%; | |
| 143 | + border: 1px solid #dcdfe6; | |
| 144 | + border-radius: 4px; | |
| 145 | + } | |
| 146 | + | |
| 147 | + .delete-icon { | |
| 148 | + position: absolute; | |
| 149 | + top: -8px; | |
| 150 | + right: -8px; | |
| 151 | + font-size: 16px; | |
| 152 | + color: #f56c6c; | |
| 153 | + background: #fff; | |
| 154 | + border-radius: 50%; | |
| 155 | + cursor: pointer; | |
| 156 | + } | |
| 157 | + } | |
| 158 | + | |
| 159 | + .upload-button { | |
| 160 | + width: 100px; | |
| 161 | + height: 100px; | |
| 162 | + display: flex; | |
| 163 | + justify-content: center; | |
| 164 | + align-items: center; | |
| 165 | + border: 1px dashed #c0ccda; | |
| 166 | + border-radius: 4px; | |
| 167 | + background-color: #fbfdff; | |
| 168 | + color: #8c939d; | |
| 169 | + font-size: 28px; | |
| 170 | + cursor: pointer; | |
| 171 | + | |
| 172 | + &:hover { | |
| 173 | + border-color: #409eff; | |
| 174 | + } | |
| 175 | + } | |
| 176 | + | |
| 177 | + .file-input { | |
| 178 | + display: none; | |
| 179 | + } | |
| 180 | + | |
| 181 | + .image-error { | |
| 182 | + width: 100%; | |
| 183 | + height: 100%; | |
| 184 | + display: flex; | |
| 185 | + justify-content: center; | |
| 186 | + align-items: center; | |
| 187 | + background-color: #f5f7fa; | |
| 188 | + | |
| 189 | + .error-image { | |
| 190 | + width: 100%; | |
| 191 | + height: 100%; | |
| 192 | + object-fit: cover; | |
| 193 | + } | |
| 194 | + } | |
| 195 | +} | |
| 196 | +</style> | |
| 0 | 197 | \ No newline at end of file | ... | ... |
src/components/room/viewImage.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="image-viewer" v-show="visible" @click.self="close"> | |
| 3 | + <div class="image-container"> | |
| 4 | + <el-image | |
| 5 | + :src="imageUrl" | |
| 6 | + fit="contain" | |
| 7 | + :style="{width: imgWidth + 'px', height: imgHeight + 'px'}" | |
| 8 | + > | |
| 9 | + <div slot="error" class="image-error"> | |
| 10 | + <img src="/img/noPhoto.jpg" :style="{width: imgWidth + 'px', height: imgHeight + 'px'}"> | |
| 11 | + </div> | |
| 12 | + </el-image> | |
| 13 | + <i class="el-icon-close close-icon" @click="close"></i> | |
| 14 | + </div> | |
| 15 | + </div> | |
| 16 | +</template> | |
| 17 | + | |
| 18 | +<script> | |
| 19 | +export default { | |
| 20 | + name: 'ViewImage', | |
| 21 | + data() { | |
| 22 | + return { | |
| 23 | + visible: false, | |
| 24 | + imageUrl: '', | |
| 25 | + imgWidth: 800, | |
| 26 | + imgHeight: 600 | |
| 27 | + } | |
| 28 | + }, | |
| 29 | + methods: { | |
| 30 | + open(url) { | |
| 31 | + this.imageUrl = url | |
| 32 | + this.visible = true | |
| 33 | + this.calculateImageSize(url) | |
| 34 | + document.body.style.overflow = 'hidden' | |
| 35 | + }, | |
| 36 | + close() { | |
| 37 | + this.visible = false | |
| 38 | + this.imageUrl = '' | |
| 39 | + document.body.style.overflow = '' | |
| 40 | + }, | |
| 41 | + calculateImageSize(url) { | |
| 42 | + const img = new Image() | |
| 43 | + img.src = url | |
| 44 | + img.onload = () => { | |
| 45 | + const maxWidth = window.innerWidth * 0.8 | |
| 46 | + const maxHeight = window.innerHeight * 0.8 | |
| 47 | + const ratio = img.width / img.height | |
| 48 | + | |
| 49 | + if (img.width > maxWidth) { | |
| 50 | + this.imgWidth = maxWidth | |
| 51 | + this.imgHeight = maxWidth / ratio | |
| 52 | + } else if (img.height > maxHeight) { | |
| 53 | + this.imgHeight = maxHeight | |
| 54 | + this.imgWidth = maxHeight * ratio | |
| 55 | + } else { | |
| 56 | + this.imgWidth = img.width | |
| 57 | + this.imgHeight = img.height | |
| 58 | + } | |
| 59 | + } | |
| 60 | + img.onerror = () => { | |
| 61 | + this.imgWidth = 800 | |
| 62 | + this.imgHeight = 600 | |
| 63 | + } | |
| 64 | + } | |
| 65 | + } | |
| 66 | +} | |
| 67 | +</script> | |
| 68 | + | |
| 69 | +<style lang="scss" scoped> | |
| 70 | +.image-viewer { | |
| 71 | + position: fixed; | |
| 72 | + top: 0; | |
| 73 | + left: 0; | |
| 74 | + width: 100%; | |
| 75 | + height: 100%; | |
| 76 | + background-color: rgba(0, 0, 0, 0.8); | |
| 77 | + z-index: 9999; | |
| 78 | + display: flex; | |
| 79 | + justify-content: center; | |
| 80 | + align-items: center; | |
| 81 | + | |
| 82 | + .image-container { | |
| 83 | + position: relative; | |
| 84 | + background-color: #fff; | |
| 85 | + padding: 20px; | |
| 86 | + border-radius: 4px; | |
| 87 | + max-width: 90vw; | |
| 88 | + max-height: 90vh; | |
| 89 | + | |
| 90 | + .close-icon { | |
| 91 | + position: absolute; | |
| 92 | + top: 10px; | |
| 93 | + right: 10px; | |
| 94 | + font-size: 24px; | |
| 95 | + color: #f56c6c; | |
| 96 | + cursor: pointer; | |
| 97 | + z-index: 1; | |
| 98 | + background-color: rgba(255, 255, 255, 0.7); | |
| 99 | + border-radius: 50%; | |
| 100 | + padding: 5px; | |
| 101 | + } | |
| 102 | + } | |
| 103 | + | |
| 104 | + .image-error { | |
| 105 | + display: flex; | |
| 106 | + justify-content: center; | |
| 107 | + align-items: center; | |
| 108 | + background-color: #f5f7fa; | |
| 109 | + width: 100%; | |
| 110 | + height: 100%; | |
| 111 | + } | |
| 112 | +} | |
| 113 | +</style> | |
| 0 | 114 | \ No newline at end of file | ... | ... |
src/components/system/addFeePrintPage.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('feePrintPageManage.add.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="50%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <el-form | |
| 9 | + ref="form" | |
| 10 | + :model="formData" | |
| 11 | + :rules="rules" | |
| 12 | + label-width="120px" | |
| 13 | + > | |
| 14 | + <el-form-item | |
| 15 | + :label="$t('feePrintPageManage.add.pageName')" | |
| 16 | + prop="pageName" | |
| 17 | + > | |
| 18 | + <el-input | |
| 19 | + v-model="formData.pageName" | |
| 20 | + :placeholder="$t('feePrintPageManage.add.pageNamePlaceholder')" | |
| 21 | + /> | |
| 22 | + </el-form-item> | |
| 23 | + <el-form-item | |
| 24 | + :label="$t('feePrintPageManage.add.template')" | |
| 25 | + prop="pageUrl" | |
| 26 | + > | |
| 27 | + <el-select | |
| 28 | + v-model="formData.pageUrl" | |
| 29 | + :placeholder="$t('feePrintPageManage.add.templatePlaceholder')" | |
| 30 | + style="width:100%" | |
| 31 | + > | |
| 32 | + <el-option | |
| 33 | + v-for="item in templates" | |
| 34 | + :key="item.templateId" | |
| 35 | + :label="item.name" | |
| 36 | + :value="item.templateId" | |
| 37 | + /> | |
| 38 | + </el-select> | |
| 39 | + </el-form-item> | |
| 40 | + </el-form> | |
| 41 | + <span slot="footer" class="dialog-footer"> | |
| 42 | + <el-button @click="visible = false"> | |
| 43 | + {{ $t('common.cancel') }} | |
| 44 | + </el-button> | |
| 45 | + <el-button | |
| 46 | + type="primary" | |
| 47 | + :loading="loading" | |
| 48 | + @click="handleSubmit" | |
| 49 | + > | |
| 50 | + {{ $t('common.confirm') }} | |
| 51 | + </el-button> | |
| 52 | + </span> | |
| 53 | + </el-dialog> | |
| 54 | +</template> | |
| 55 | + | |
| 56 | +<script> | |
| 57 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 58 | +import { saveFeePrintPage, listFeePrintPageTemplate } from '@/api/system/feePrintPageManageApi' | |
| 59 | + | |
| 60 | +export default { | |
| 61 | + name: 'AddFeePrintPage', | |
| 62 | + data() { | |
| 63 | + return { | |
| 64 | + visible: false, | |
| 65 | + loading: false, | |
| 66 | + communityId: '', | |
| 67 | + formData: { | |
| 68 | + pageName: '', | |
| 69 | + pageUrl: '', | |
| 70 | + communityId: '' | |
| 71 | + }, | |
| 72 | + templates: [], | |
| 73 | + rules: { | |
| 74 | + pageName: [ | |
| 75 | + { required: true, message: this.$t('feePrintPageManage.validate.pageNameRequired'), trigger: 'blur' }, | |
| 76 | + { max: 128, message: this.$t('feePrintPageManage.validate.pageNameMaxLength'), trigger: 'blur' } | |
| 77 | + ], | |
| 78 | + pageUrl: [ | |
| 79 | + { required: true, message: this.$t('feePrintPageManage.validate.templateRequired'), trigger: 'change' } | |
| 80 | + ] | |
| 81 | + } | |
| 82 | + } | |
| 83 | + }, | |
| 84 | + methods: { | |
| 85 | + open() { | |
| 86 | + this.communityId = getCommunityId() | |
| 87 | + this.formData.communityId = this.communityId | |
| 88 | + this.getTemplates() | |
| 89 | + this.visible = true | |
| 90 | + }, | |
| 91 | + async getTemplates() { | |
| 92 | + try { | |
| 93 | + const params = { | |
| 94 | + page: 1, | |
| 95 | + row: 50, | |
| 96 | + communityId: this.communityId | |
| 97 | + } | |
| 98 | + const { data } = await listFeePrintPageTemplate(params) | |
| 99 | + this.templates = data | |
| 100 | + } catch (error) { | |
| 101 | + console.error('获取模板列表失败:', error) | |
| 102 | + } | |
| 103 | + }, | |
| 104 | + handleSubmit() { | |
| 105 | + this.$refs.form.validate(async valid => { | |
| 106 | + if (valid) { | |
| 107 | + try { | |
| 108 | + this.loading = true | |
| 109 | + await saveFeePrintPage(this.formData) | |
| 110 | + this.$message.success(this.$t('feePrintPageManage.add.success')) | |
| 111 | + this.$emit('success') | |
| 112 | + this.visible = false | |
| 113 | + } catch (error) { | |
| 114 | + console.error('添加失败:', error) | |
| 115 | + } finally { | |
| 116 | + this.loading = false | |
| 117 | + } | |
| 118 | + } | |
| 119 | + }) | |
| 120 | + }, | |
| 121 | + handleClose() { | |
| 122 | + this.$refs.form.resetFields() | |
| 123 | + this.formData = { | |
| 124 | + pageName: '', | |
| 125 | + pageUrl: '', | |
| 126 | + communityId: this.communityId | |
| 127 | + } | |
| 128 | + } | |
| 129 | + } | |
| 130 | +} | |
| 131 | +</script> | |
| 0 | 132 | \ No newline at end of file | ... | ... |
src/components/system/deleteDownloadTempFile.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('deleteDownloadTempFile.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="30%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <div style="text-align: center;"> | |
| 9 | + <p>{{ $t('deleteDownloadTempFile.confirmMessage') }}</p> | |
| 10 | + </div> | |
| 11 | + <span slot="footer" class="dialog-footer"> | |
| 12 | + <el-button @click="handleClose">{{ $t('deleteDownloadTempFile.cancel') }}</el-button> | |
| 13 | + <el-button type="primary" @click="handleDelete">{{ $t('deleteDownloadTempFile.confirm') }}</el-button> | |
| 14 | + </span> | |
| 15 | + </el-dialog> | |
| 16 | +</template> | |
| 17 | + | |
| 18 | +<script> | |
| 19 | +import { deleteUserDownloadFile } from '@/api/system/downloadTempFileApi' | |
| 20 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 21 | + | |
| 22 | +export default { | |
| 23 | + name: 'DeleteDownloadTempFile', | |
| 24 | + data() { | |
| 25 | + return { | |
| 26 | + visible: false, | |
| 27 | + currentFile: null | |
| 28 | + } | |
| 29 | + }, | |
| 30 | + methods: { | |
| 31 | + open(file) { | |
| 32 | + this.currentFile = file | |
| 33 | + this.visible = true | |
| 34 | + }, | |
| 35 | + handleClose() { | |
| 36 | + this.visible = false | |
| 37 | + this.currentFile = null | |
| 38 | + }, | |
| 39 | + async handleDelete() { | |
| 40 | + try { | |
| 41 | + const params = { | |
| 42 | + ...this.currentFile, | |
| 43 | + communityId: getCommunityId() | |
| 44 | + } | |
| 45 | + await deleteUserDownloadFile(params) | |
| 46 | + this.$emit('success') | |
| 47 | + this.$message.success(this.$t('deleteDownloadTempFile.deleteSuccess')) | |
| 48 | + this.handleClose() | |
| 49 | + } catch (error) { | |
| 50 | + this.$message.error(this.$t('deleteDownloadTempFile.deleteFailed')) | |
| 51 | + } | |
| 52 | + } | |
| 53 | + } | |
| 54 | +} | |
| 55 | +</script> | |
| 0 | 56 | \ No newline at end of file | ... | ... |
src/components/system/deleteFeePrintPage.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('feePrintPageManage.delete.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="30%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <div class="delete-content"> | |
| 9 | + <i class="el-icon-warning" style="color:#E6A23C;font-size:24px;vertical-align:middle;"></i> | |
| 10 | + <span style="margin-left:10px;vertical-align:middle;"> | |
| 11 | + {{ $t('feePrintPageManage.delete.confirmText') }} | |
| 12 | + </span> | |
| 13 | + </div> | |
| 14 | + <span slot="footer" class="dialog-footer"> | |
| 15 | + <el-button @click="visible = false"> | |
| 16 | + {{ $t('common.cancel') }} | |
| 17 | + </el-button> | |
| 18 | + <el-button | |
| 19 | + type="primary" | |
| 20 | + :loading="loading" | |
| 21 | + @click="handleConfirm" | |
| 22 | + > | |
| 23 | + {{ $t('common.confirm') }} | |
| 24 | + </el-button> | |
| 25 | + </span> | |
| 26 | + </el-dialog> | |
| 27 | +</template> | |
| 28 | + | |
| 29 | +<script> | |
| 30 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 31 | +import { deleteFeePrintPage } from '@/api/system/feePrintPageManageApi' | |
| 32 | + | |
| 33 | +export default { | |
| 34 | + name: 'DeleteFeePrintPage', | |
| 35 | + data() { | |
| 36 | + return { | |
| 37 | + visible: false, | |
| 38 | + loading: false, | |
| 39 | + communityId: '', | |
| 40 | + currentRow: { | |
| 41 | + pageId: '', | |
| 42 | + communityId: '' | |
| 43 | + } | |
| 44 | + } | |
| 45 | + }, | |
| 46 | + methods: { | |
| 47 | + open(row) { | |
| 48 | + this.communityId = getCommunityId() | |
| 49 | + this.currentRow = { | |
| 50 | + pageId: row.pageId, | |
| 51 | + communityId: this.communityId | |
| 52 | + } | |
| 53 | + this.visible = true | |
| 54 | + }, | |
| 55 | + async handleConfirm() { | |
| 56 | + try { | |
| 57 | + this.loading = true | |
| 58 | + await deleteFeePrintPage(this.currentRow) | |
| 59 | + this.$message.success(this.$t('feePrintPageManage.delete.success')) | |
| 60 | + this.$emit('success') | |
| 61 | + this.visible = false | |
| 62 | + } catch (error) { | |
| 63 | + console.error('删除失败:', error) | |
| 64 | + } finally { | |
| 65 | + this.loading = false | |
| 66 | + } | |
| 67 | + }, | |
| 68 | + handleClose() { | |
| 69 | + this.currentRow = { | |
| 70 | + pageId: '', | |
| 71 | + communityId: '' | |
| 72 | + } | |
| 73 | + } | |
| 74 | + } | |
| 75 | +} | |
| 76 | +</script> | |
| 77 | + | |
| 78 | +<style scoped> | |
| 79 | +.delete-content { | |
| 80 | + padding: 10px 20px; | |
| 81 | + font-size: 16px; | |
| 82 | +} | |
| 83 | +</style> | |
| 0 | 84 | \ No newline at end of file | ... | ... |
src/components/system/editFeePrintPage.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('feePrintPageManage.edit.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="50%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <el-form | |
| 9 | + ref="form" | |
| 10 | + :model="formData" | |
| 11 | + :rules="rules" | |
| 12 | + label-width="120px" | |
| 13 | + > | |
| 14 | + <el-form-item | |
| 15 | + :label="$t('feePrintPageManage.edit.pageName')" | |
| 16 | + prop="pageName" | |
| 17 | + > | |
| 18 | + <el-input | |
| 19 | + v-model="formData.pageName" | |
| 20 | + :placeholder="$t('feePrintPageManage.edit.pageNamePlaceholder')" | |
| 21 | + /> | |
| 22 | + </el-form-item> | |
| 23 | + <el-form-item | |
| 24 | + :label="$t('feePrintPageManage.edit.template')" | |
| 25 | + prop="pageUrl" | |
| 26 | + > | |
| 27 | + <el-select | |
| 28 | + v-model="formData.pageUrl" | |
| 29 | + :placeholder="$t('feePrintPageManage.edit.templatePlaceholder')" | |
| 30 | + style="width:100%" | |
| 31 | + > | |
| 32 | + <el-option | |
| 33 | + v-for="item in templates" | |
| 34 | + :key="item.templateId" | |
| 35 | + :label="item.name" | |
| 36 | + :value="item.templateId" | |
| 37 | + /> | |
| 38 | + </el-select> | |
| 39 | + </el-form-item> | |
| 40 | + </el-form> | |
| 41 | + <span slot="footer" class="dialog-footer"> | |
| 42 | + <el-button @click="visible = false"> | |
| 43 | + {{ $t('common.cancel') }} | |
| 44 | + </el-button> | |
| 45 | + <el-button | |
| 46 | + type="primary" | |
| 47 | + :loading="loading" | |
| 48 | + @click="handleSubmit" | |
| 49 | + > | |
| 50 | + {{ $t('common.confirm') }} | |
| 51 | + </el-button> | |
| 52 | + </span> | |
| 53 | + </el-dialog> | |
| 54 | +</template> | |
| 55 | + | |
| 56 | +<script> | |
| 57 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 58 | +import { updateFeePrintPage, listFeePrintPageTemplate } from '@/api/system/feePrintPageManageApi' | |
| 59 | + | |
| 60 | +export default { | |
| 61 | + name: 'EditFeePrintPage', | |
| 62 | + data() { | |
| 63 | + return { | |
| 64 | + visible: false, | |
| 65 | + loading: false, | |
| 66 | + communityId: '', | |
| 67 | + formData: { | |
| 68 | + pageId: '', | |
| 69 | + pageName: '', | |
| 70 | + pageUrl: '', | |
| 71 | + communityId: '' | |
| 72 | + }, | |
| 73 | + templates: [], | |
| 74 | + rules: { | |
| 75 | + pageName: [ | |
| 76 | + { required: true, message: this.$t('feePrintPageManage.validate.pageNameRequired'), trigger: 'blur' }, | |
| 77 | + { max: 128, message: this.$t('feePrintPageManage.validate.pageNameMaxLength'), trigger: 'blur' } | |
| 78 | + ], | |
| 79 | + pageUrl: [ | |
| 80 | + { required: true, message: this.$t('feePrintPageManage.validate.templateRequired'), trigger: 'change' } | |
| 81 | + ], | |
| 82 | + pageId: [ | |
| 83 | + { required: true, message: this.$t('feePrintPageManage.validate.pageIdRequired'), trigger: 'blur' } | |
| 84 | + ] | |
| 85 | + } | |
| 86 | + } | |
| 87 | + }, | |
| 88 | + methods: { | |
| 89 | + open(row) { | |
| 90 | + this.communityId = getCommunityId() | |
| 91 | + this.formData = { | |
| 92 | + pageId: row.pageId, | |
| 93 | + pageName: row.pageName, | |
| 94 | + pageUrl: row.pageUrl, | |
| 95 | + communityId: this.communityId | |
| 96 | + } | |
| 97 | + this.getTemplates() | |
| 98 | + this.visible = true | |
| 99 | + }, | |
| 100 | + async getTemplates() { | |
| 101 | + try { | |
| 102 | + const params = { | |
| 103 | + page: 1, | |
| 104 | + row: 50, | |
| 105 | + communityId: this.communityId | |
| 106 | + } | |
| 107 | + const { data } = await listFeePrintPageTemplate(params) | |
| 108 | + this.templates = data | |
| 109 | + } catch (error) { | |
| 110 | + console.error('获取模板列表失败:', error) | |
| 111 | + } | |
| 112 | + }, | |
| 113 | + handleSubmit() { | |
| 114 | + this.$refs.form.validate(async valid => { | |
| 115 | + if (valid) { | |
| 116 | + try { | |
| 117 | + this.loading = true | |
| 118 | + await updateFeePrintPage(this.formData) | |
| 119 | + this.$message.success(this.$t('feePrintPageManage.edit.success')) | |
| 120 | + this.$emit('success') | |
| 121 | + this.visible = false | |
| 122 | + } catch (error) { | |
| 123 | + console.error('更新失败:', error) | |
| 124 | + } finally { | |
| 125 | + this.loading = false | |
| 126 | + } | |
| 127 | + } | |
| 128 | + }) | |
| 129 | + }, | |
| 130 | + handleClose() { | |
| 131 | + this.$refs.form.resetFields() | |
| 132 | + this.formData = { | |
| 133 | + pageId: '', | |
| 134 | + pageName: '', | |
| 135 | + pageUrl: '', | |
| 136 | + communityId: this.communityId | |
| 137 | + } | |
| 138 | + } | |
| 139 | + } | |
| 140 | +} | |
| 141 | +</script> | |
| 0 | 142 | \ No newline at end of file | ... | ... |
src/components/upload/UploadImageUrl.md
0 → 100644
| 1 | +# UploadImageUrl 组件文档 | |
| 2 | + | |
| 3 | +## 组件概述 | |
| 4 | + | |
| 5 | +`UploadImageUrl` 是一个图片上传组件,支持上传本地图片并显示预览,同时可以删除已上传的图片。 | |
| 6 | + | |
| 7 | +## 基本用法 | |
| 8 | + | |
| 9 | +```vue | |
| 10 | +<template> | |
| 11 | + <upload-image-url | |
| 12 | + :image-count="3" | |
| 13 | + @notifyUploadCoverImage="handleImageUpload" | |
| 14 | + /> | |
| 15 | +</template> | |
| 16 | + | |
| 17 | +<script> | |
| 18 | +import UploadImageUrl from '@/components/staff/UploadImageUrl.vue' | |
| 19 | + | |
| 20 | +export default { | |
| 21 | + components: { | |
| 22 | + UploadImageUrl | |
| 23 | + }, | |
| 24 | + methods: { | |
| 25 | + handleImageUpload(urls) { | |
| 26 | + // 处理上传后的图片URL数组 | |
| 27 | + console.log(urls) | |
| 28 | + } | |
| 29 | + } | |
| 30 | +} | |
| 31 | +</script> | |
| 32 | +``` | |
| 33 | + | |
| 34 | +## Props | |
| 35 | + | |
| 36 | +| 参数名 | 说明 | 类型 | 默认值 | | |
| 37 | +|------------|--------------------|----------|--------| | |
| 38 | +| imageCount | 允许上传的最大图片数量 | Number | 1 | | |
| 39 | + | |
| 40 | +## Events | |
| 41 | + | |
| 42 | +| 事件名 | 说明 | 回调参数 | | |
| 43 | +|----------------------|--------------------------|-----------------------| | |
| 44 | +| notifyUploadCoverImage | 图片上传或删除时触发 | 当前所有图片的URL数组 | | |
| 45 | + | |
| 46 | +## 方法 | |
| 47 | + | |
| 48 | +通过 ref 可以调用组件的方法: | |
| 49 | + | |
| 50 | +```vue | |
| 51 | +<template> | |
| 52 | + <upload-image-url ref="uploader" /> | |
| 53 | +</template> | |
| 54 | + | |
| 55 | +<script> | |
| 56 | +export default { | |
| 57 | + methods: { | |
| 58 | + clearImages() { | |
| 59 | + this.$refs.uploader.clearImages() | |
| 60 | + }, | |
| 61 | + setImages(images) { | |
| 62 | + this.$refs.uploader.setImages(images) | |
| 63 | + } | |
| 64 | + } | |
| 65 | +} | |
| 66 | +</script> | |
| 67 | +``` | |
| 68 | + | |
| 69 | +| 方法名 | 说明 | 参数 | | |
| 70 | +|-------------|--------------------------|--------------------------| | |
| 71 | +| clearImages | 清空所有已上传的图片 | 无 | | |
| 72 | +| setImages | 设置要显示的图片 | images: Array<string> (图片URL数组) | | |
| 73 | + | |
| 74 | +## 样式定制 | |
| 75 | + | |
| 76 | +组件使用 SCSS 编写样式,可以通过以下类名进行样式覆盖: | |
| 77 | + | |
| 78 | +- `.upload-image-container` - 整个上传容器 | |
| 79 | +- `.image-item` - 单个图片项容器 | |
| 80 | +- `.delete-icon` - 删除按钮 | |
| 81 | +- `.upload-button` - 上传按钮 | |
| 82 | + | |
| 83 | +## 注意事项 | |
| 84 | + | |
| 85 | +1. 组件限制上传图片大小不超过 2MB | |
| 86 | +2. 上传的图片会自动转换为 base64 格式进行预览 | |
| 87 | +3. 组件内部会维护两个数组: | |
| 88 | + - `photos`: 用于预览的图片数组(包含 base64 数据) | |
| 89 | + - `photosUrl`: 上传后的图片 URL 数组 | |
| 90 | +4. 当图片 URL 以 `http` 开头时,组件会直接使用该 URL 进行显示 | |
| 91 | +5. 当图片数据包含 `base64,` 时,组件会将其视为 base64 数据进行显示 | |
| 92 | + | |
| 93 | +## 示例 | |
| 94 | + | |
| 95 | +### 基本使用 | |
| 96 | + | |
| 97 | +```vue | |
| 98 | +<upload-image-url /> | |
| 99 | +``` | |
| 100 | + | |
| 101 | +### 限制上传数量 | |
| 102 | + | |
| 103 | +```vue | |
| 104 | +<upload-image-url :image-count="5" /> | |
| 105 | +``` | |
| 106 | + | |
| 107 | +### 获取上传结果 | |
| 108 | + | |
| 109 | +```vue | |
| 110 | +<template> | |
| 111 | + <upload-image-url @notifyUploadCoverImage="handleUpload" /> | |
| 112 | +</template> | |
| 113 | + | |
| 114 | +<script> | |
| 115 | +export default { | |
| 116 | + methods: { | |
| 117 | + handleUpload(urls) { | |
| 118 | + console.log('上传的图片URL:', urls) | |
| 119 | + // 可以将这些URL保存到表单数据中 | |
| 120 | + } | |
| 121 | + } | |
| 122 | +} | |
| 123 | +</script> | |
| 124 | +``` | |
| 125 | + | |
| 126 | +### 设置初始图片 | |
| 127 | + | |
| 128 | +```vue | |
| 129 | +<template> | |
| 130 | + <upload-image-url ref="uploader" /> | |
| 131 | +</template> | |
| 132 | + | |
| 133 | +<script> | |
| 134 | +export default { | |
| 135 | + mounted() { | |
| 136 | + // 设置初始图片 | |
| 137 | + this.$refs.uploader.setImages([ | |
| 138 | + 'https://example.com/image1.jpg', | |
| 139 | + 'https://example.com/image2.jpg' | |
| 140 | + ]) | |
| 141 | + } | |
| 142 | +} | |
| 143 | +</script> | |
| 144 | +``` | |
| 145 | + | |
| 146 | +### 清空图片 | |
| 147 | + | |
| 148 | +```vue | |
| 149 | +<template> | |
| 150 | + <upload-image-url ref="uploader" /> | |
| 151 | + <button @click="clear">清空图片</button> | |
| 152 | +</template> | |
| 153 | + | |
| 154 | +<script> | |
| 155 | +export default { | |
| 156 | + methods: { | |
| 157 | + clear() { | |
| 158 | + this.$refs.uploader.clearImages() | |
| 159 | + } | |
| 160 | + } | |
| 161 | +} | |
| 162 | +</script> | |
| 163 | +``` | |
| 0 | 164 | \ No newline at end of file | ... | ... |
src/i18n/commonLang.js
| ... | ... | @@ -11,6 +11,7 @@ export const messages = { |
| 11 | 11 | delete: 'Delete', |
| 12 | 12 | confirm: 'Confirm', |
| 13 | 13 | cancel: 'Cancel', |
| 14 | + close: 'Close', | |
| 14 | 15 | back: 'Back', |
| 15 | 16 | warning: 'Warning', |
| 16 | 17 | deleteConfirm: 'Are you sure to delete this record?', |
| ... | ... | @@ -50,6 +51,7 @@ export const messages = { |
| 50 | 51 | print:'Print', |
| 51 | 52 | year:'Year', |
| 52 | 53 | month:'Month', |
| 54 | + examine:'Examine', | |
| 53 | 55 | } |
| 54 | 56 | }, |
| 55 | 57 | zh: { |
| ... | ... | @@ -64,6 +66,7 @@ export const messages = { |
| 64 | 66 | delete: '删除', |
| 65 | 67 | confirm: '确定', |
| 66 | 68 | cancel: '取消', |
| 69 | + close: '关闭', | |
| 67 | 70 | back: '返回', |
| 68 | 71 | warning: '警告', |
| 69 | 72 | deleteConfirm: '确定要删除这条记录吗?', |
| ... | ... | @@ -103,6 +106,7 @@ export const messages = { |
| 103 | 106 | print:'打印', |
| 104 | 107 | year:'年', |
| 105 | 108 | month:'月', |
| 109 | + examine:'审核', | |
| 106 | 110 | } |
| 107 | 111 | } |
| 108 | 112 | } |
| 109 | 113 | \ No newline at end of file | ... | ... |
src/i18n/communityI18n.js
0 → 100644
| 1 | +import { messages as roomStructureMessages } from '../views/room/roomStructureLang' | |
| 2 | +import { messages as carStructureMessages } from '../views/car/carStructureLang' | |
| 3 | +import { messages as propertyRightRegistrationManageMessages } from '../views/room/propertyRightRegistrationManageLang' | |
| 4 | +import { messages as listPropertyRightRegistrationDetailMessages } from '../views/room/listPropertyRightRegistrationDetailLang' | |
| 5 | +export const messages = { | |
| 6 | + en: { | |
| 7 | + ...roomStructureMessages.en, | |
| 8 | + ...carStructureMessages.en, | |
| 9 | + ...propertyRightRegistrationManageMessages.en, | |
| 10 | + ...listPropertyRightRegistrationDetailMessages.en, | |
| 11 | + }, | |
| 12 | + zh: { | |
| 13 | + ...roomStructureMessages.zh, | |
| 14 | + ...carStructureMessages.zh, | |
| 15 | + ...propertyRightRegistrationManageMessages.zh, | |
| 16 | + ...listPropertyRightRegistrationDetailMessages.zh, | |
| 17 | + } | |
| 18 | +} | |
| 0 | 19 | \ No newline at end of file | ... | ... |
src/i18n/index.js
| ... | ... | @@ -144,6 +144,7 @@ import { messages as carI18n } from './carI18n' |
| 144 | 144 | import { messages as scmI18n } from './scmI18n' |
| 145 | 145 | import { messages as userI18n } from './userI18n' |
| 146 | 146 | import { messages as systemI18n } from './systemI18n' |
| 147 | +import { messages as communityI18n } from './communityI18n' | |
| 147 | 148 | |
| 148 | 149 | Vue.use(VueI18n) |
| 149 | 150 | |
| ... | ... | @@ -286,6 +287,7 @@ const messages = { |
| 286 | 287 | ...scmI18n.en, |
| 287 | 288 | ...userI18n.en, |
| 288 | 289 | ...systemI18n.en, |
| 290 | + ...communityI18n.en, | |
| 289 | 291 | }, |
| 290 | 292 | zh: { |
| 291 | 293 | ...loginMessages.zh, |
| ... | ... | @@ -422,6 +424,7 @@ const messages = { |
| 422 | 424 | ...scmI18n.zh, |
| 423 | 425 | ...userI18n.zh, |
| 424 | 426 | ...systemI18n.zh, |
| 427 | + ...communityI18n.zh, | |
| 425 | 428 | } |
| 426 | 429 | } |
| 427 | 430 | ... | ... |
src/i18n/machineI18n.js
| ... | ... | @@ -7,6 +7,7 @@ import { messages as equipmentAccountMessages } from '../views/machine/equipment |
| 7 | 7 | import { messages as addEquipmentAccountMessages } from '../views/machine/addEquipmentAccountLang' |
| 8 | 8 | import { messages as editEquipmentAccountMessages } from '../views/machine/editEquipmentAccountLang' |
| 9 | 9 | import { messages as equipmentAccountDetailMessages } from '../views/machine/equipmentAccountDetailLang' |
| 10 | +import { messages as videoControlMessages } from '../views/machine/videoControlLang' | |
| 10 | 11 | export const messages ={ |
| 11 | 12 | en:{ |
| 12 | 13 | ...machineTypeTreeManageMessages.en, |
| ... | ... | @@ -18,6 +19,7 @@ export const messages ={ |
| 18 | 19 | ...accessControlInoutMessages.en, |
| 19 | 20 | ...machinePrinterManageMessages.en, |
| 20 | 21 | ...printerRuleMessages.en, |
| 22 | + ...videoControlMessages.en, | |
| 21 | 23 | }, |
| 22 | 24 | zh:{ |
| 23 | 25 | ...machineTypeTreeManageMessages.zh, |
| ... | ... | @@ -29,5 +31,6 @@ export const messages ={ |
| 29 | 31 | ...accessControlInoutMessages.zh, |
| 30 | 32 | ...machinePrinterManageMessages.zh, |
| 31 | 33 | ...printerRuleMessages.zh, |
| 34 | + ...videoControlMessages.zh, | |
| 32 | 35 | } |
| 33 | 36 | } |
| 34 | 37 | \ No newline at end of file | ... | ... |
src/i18n/systemI18n.js
| ... | ... | @@ -6,6 +6,12 @@ import { messages as paymentPoolMessages } from '../views/system/paymentPoolLang |
| 6 | 6 | import { messages as operateDataLogMessages } from '../views/system/operateDataLogLang' |
| 7 | 7 | import { messages as historyFeeDetailImportMessages } from '../views/system/historyFeeDetailImportLang' |
| 8 | 8 | import { messages as feePrintSpecManageMessages } from '../views/system/feePrintSpecManageLang' |
| 9 | +import { messages as feePrintPageManageMessages } from '../views/system/feePrintPageManageLang' | |
| 10 | +import { messages as downloadTempFileMessages } from '../views/system/downloadTempFileLang' | |
| 11 | +import { messages as assetImportLogMessages } from '../views/system/assetImportLogLang' | |
| 12 | +import { messages as assetImportLogDetailMessages } from '../views/system/assetImportLogDetailLang' | |
| 13 | +import { messages as payFeeQrcodeMessages } from '../views/fee/payFeeQrcodeLang' | |
| 14 | + | |
| 9 | 15 | export const messages = { |
| 10 | 16 | en: { |
| 11 | 17 | ...communitySettingManageMessages.en, |
| ... | ... | @@ -16,6 +22,11 @@ export const messages = { |
| 16 | 22 | ...operateDataLogMessages.en, |
| 17 | 23 | ...historyFeeDetailImportMessages.en, |
| 18 | 24 | ...feePrintSpecManageMessages.en, |
| 25 | + ...feePrintPageManageMessages.en, | |
| 26 | + ...downloadTempFileMessages.en, | |
| 27 | + ...assetImportLogMessages.en, | |
| 28 | + ...assetImportLogDetailMessages.en, | |
| 29 | + ...payFeeQrcodeMessages.en, | |
| 19 | 30 | }, |
| 20 | 31 | zh: { |
| 21 | 32 | ...communitySettingManageMessages.zh, |
| ... | ... | @@ -26,5 +37,10 @@ export const messages = { |
| 26 | 37 | ...operateDataLogMessages.zh, |
| 27 | 38 | ...historyFeeDetailImportMessages.zh, |
| 28 | 39 | ...feePrintSpecManageMessages.zh, |
| 40 | + ...feePrintPageManageMessages.zh, | |
| 41 | + ...downloadTempFileMessages.zh, | |
| 42 | + ...assetImportLogMessages.zh, | |
| 43 | + ...assetImportLogDetailMessages.zh, | |
| 44 | + ...payFeeQrcodeMessages.zh, | |
| 29 | 45 | } |
| 30 | 46 | } |
| 31 | 47 | \ No newline at end of file | ... | ... |
src/main.js
| ... | ... | @@ -6,6 +6,12 @@ import 'element-ui/lib/theme-chalk/index.css' |
| 6 | 6 | import i18n from './i18n' |
| 7 | 7 | import {getCommunityName,getCommunityId} from '@/api/community/communityApi' |
| 8 | 8 | |
| 9 | +// 验证全局脚本是否正确加载 | |
| 10 | +console.log('检查全局脚本加载状态:') | |
| 11 | +console.log('Qs 库:', typeof window.Qs !== 'undefined' ? '已加载' : '未加载') | |
| 12 | +if (typeof window.Qs !== 'undefined') { | |
| 13 | + console.log('Qs 版本:', window.Qs.VERSION || '未知版本') | |
| 14 | +} | |
| 9 | 15 | |
| 10 | 16 | Vue.prototype.getCommunityId = function(){ |
| 11 | 17 | return getCommunityId() | ... | ... |
src/router/communityRouter.js
0 → 100644
| 1 | +export default [ | |
| 2 | + { | |
| 3 | + path: '/pages/property/roomStructure', | |
| 4 | + name: '/pages/property/roomStructure', | |
| 5 | + component: () => import('@/views/room/roomStructureList.vue') | |
| 6 | + }, | |
| 7 | + { | |
| 8 | + path: '/pages/property/carStructure', | |
| 9 | + name: '/pages/property/carStructure', | |
| 10 | + component: () => import('@/views/car/carStructureList.vue') | |
| 11 | + }, | |
| 12 | + { | |
| 13 | + path: '/pages/property/propertyRightRegistrationManage', | |
| 14 | + name: '/pages/property/propertyRightRegistrationManage', | |
| 15 | + component: () => import('@/views/room/propertyRightRegistrationManageList.vue') | |
| 16 | + }, | |
| 17 | + { | |
| 18 | + path: '/views/room/listPropertyRightRegistrationDetail', | |
| 19 | + name: '/views/room/listPropertyRightRegistrationDetail', | |
| 20 | + component: () => import('@/views/room/listPropertyRightRegistrationDetailList.vue') | |
| 21 | + }, | |
| 22 | +] | |
| 0 | 23 | \ No newline at end of file | ... | ... |
src/router/index.js
| ... | ... | @@ -14,6 +14,7 @@ import carRouter from './carRouter' |
| 14 | 14 | import scmRouter from './scmRouter' |
| 15 | 15 | import userRouter from './userRouter' |
| 16 | 16 | import systemRouter from './systemRouter' |
| 17 | +import communityRouter from './communityRouter' | |
| 17 | 18 | |
| 18 | 19 | Vue.use(VueRouter) |
| 19 | 20 | |
| ... | ... | @@ -636,6 +637,7 @@ const routes = [ |
| 636 | 637 | ...scmRouter, |
| 637 | 638 | ...userRouter, |
| 638 | 639 | ...systemRouter, |
| 640 | + ...communityRouter, | |
| 639 | 641 | // 其他子路由可以在这里添加 |
| 640 | 642 | ] |
| 641 | 643 | }, | ... | ... |
src/router/machineRouter.js
| ... | ... | @@ -49,4 +49,9 @@ export default [ |
| 49 | 49 | name: '/pages/print/printerRule', |
| 50 | 50 | component: () => import('@/views/machine/printerRuleList.vue') |
| 51 | 51 | }, |
| 52 | + { | |
| 53 | + path: '/pages/property/videoControl', | |
| 54 | + name: '/pages/property/videoControl', | |
| 55 | + component: () => import('@/views/machine/videoControlList.vue') | |
| 56 | + }, | |
| 52 | 57 | ] |
| 53 | 58 | \ No newline at end of file | ... | ... |
src/router/systemRouter.js
| ... | ... | @@ -49,4 +49,29 @@ export default [ |
| 49 | 49 | name: '/pages/property/feePrintSpecManage', |
| 50 | 50 | component: () => import('@/views/system/feePrintSpecManageList.vue') |
| 51 | 51 | }, |
| 52 | + { | |
| 53 | + path: '/pages/property/feePrintPageManage', | |
| 54 | + name: '/pages/property/feePrintPageManage', | |
| 55 | + component: () => import('@/views/system/feePrintPageManageList.vue') | |
| 56 | + }, | |
| 57 | + { | |
| 58 | + path: '/pages/property/downloadTempFile', | |
| 59 | + name: '/pages/property/downloadTempFile', | |
| 60 | + component: () => import('@/views/system/downloadTempFileList.vue') | |
| 61 | + }, | |
| 62 | + { | |
| 63 | + path: '/pages/property/assetImportLog', | |
| 64 | + name: '/pages/property/assetImportLog', | |
| 65 | + component: () => import('@/views/system/assetImportLogList.vue') | |
| 66 | + }, | |
| 67 | + { | |
| 68 | + path: '/views/system/assetImportLogDetail', | |
| 69 | + name: '/views/system/assetImportLogDetail', | |
| 70 | + component: () => import('@/views/system/assetImportLogDetailList.vue') | |
| 71 | + }, | |
| 72 | + { | |
| 73 | + path: '/pages/fee/payFeeQrcode', | |
| 74 | + name: '/pages/fee/payFeeQrcode', | |
| 75 | + component: () => import('@/views/fee/payFeeQrcodeList.vue') | |
| 76 | + }, | |
| 52 | 77 | ] |
| 53 | 78 | \ No newline at end of file | ... | ... |
src/views/car/carStructureLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + carStructure: { | |
| 4 | + noOwner: 'No Owner', | |
| 5 | + oweAmount: 'Arrears', | |
| 6 | + yuan: 'Yuan', | |
| 7 | + searchPlaceholder: 'Please enter house number building-unit-room such as 1-1-1', | |
| 8 | + fetchError: 'Failed to fetch car structure data' | |
| 9 | + }, | |
| 10 | + floorUnitTree: { | |
| 11 | + noBuilding: 'No building currently', | |
| 12 | + building: 'Building', | |
| 13 | + unit: 'Unit', | |
| 14 | + fetchError: 'Failed to fetch floor and unit data' | |
| 15 | + } | |
| 16 | + }, | |
| 17 | + zh: { | |
| 18 | + carStructure: { | |
| 19 | + noOwner: '无', | |
| 20 | + oweAmount: '欠费', | |
| 21 | + yuan: '元', | |
| 22 | + searchPlaceholder: '请输入房屋编号 楼栋-单元-房屋 如1-1-1', | |
| 23 | + fetchError: '获取车位结构数据失败' | |
| 24 | + }, | |
| 25 | + floorUnitTree: { | |
| 26 | + noBuilding: '当前没有楼栋', | |
| 27 | + building: '栋', | |
| 28 | + unit: '单元', | |
| 29 | + fetchError: '获取楼栋单元数据失败' | |
| 30 | + } | |
| 31 | + } | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
src/views/car/carStructureList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="car-structure-container"> | |
| 3 | + <el-row :gutter="20"> | |
| 4 | + <el-col :span="4"> | |
| 5 | + <floor-unit-tree ref="floorUnitTree" @switchFloorUnit="switchFloorUnit" /> | |
| 6 | + </el-col> | |
| 7 | + <el-col :span="20"> | |
| 8 | + <el-card class="box-card margin-bottom car-list-card"> | |
| 9 | + <el-row :gutter="10" class="margin-top"> | |
| 10 | + <el-col v-for="(car, index) in carStructureInfo.cars" :key="index" :span="4" | |
| 11 | + class="text-center margin-bottom padding car-card" :style="{ 'background-color': getBgColor(car) }" | |
| 12 | + @dblclick.native="toSimplifyAcceptance(car)"> | |
| 13 | + <div>{{ car.areaNum }}-{{ car.num }}</div> | |
| 14 | + <div>{{ car.carNum }}</div> | |
| 15 | + <div>{{ car.floorNum }}-{{ car.unitNum }}-{{ car.roomNum }}</div> | |
| 16 | + <div>{{ car.ownerName || $t('carStructure.noOwner') }}</div> | |
| 17 | + <div> | |
| 18 | + <span>{{ $t('carStructure.oweAmount') }}</span>:{{ car.oweAmount }} | |
| 19 | + <span>{{ $t('carStructure.yuan') }}</span> | |
| 20 | + </div> | |
| 21 | + </el-col> | |
| 22 | + </el-row> | |
| 23 | + </el-card> | |
| 24 | + </el-col> | |
| 25 | + </el-row> | |
| 26 | + </div> | |
| 27 | +</template> | |
| 28 | + | |
| 29 | +<script> | |
| 30 | +import floorUnitTree from '@/components/room/floorUnitTree' | |
| 31 | +import { listCarStructure } from '@/api/car/carStructureApi' | |
| 32 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 33 | + | |
| 34 | +export default { | |
| 35 | + name: 'CarStructureList', | |
| 36 | + components: { | |
| 37 | + floorUnitTree | |
| 38 | + }, | |
| 39 | + data() { | |
| 40 | + return { | |
| 41 | + carStructureInfo: { | |
| 42 | + cars: [] | |
| 43 | + }, | |
| 44 | + communityId: '' | |
| 45 | + } | |
| 46 | + }, | |
| 47 | + created() { | |
| 48 | + this.communityId = getCommunityId() | |
| 49 | + }, | |
| 50 | + methods: { | |
| 51 | + switchFloorUnit(data) { | |
| 52 | + if (!data.unitId) { | |
| 53 | + return | |
| 54 | + } | |
| 55 | + this.loadCars(data.unitId) | |
| 56 | + }, | |
| 57 | + async loadCars(unitId) { | |
| 58 | + try { | |
| 59 | + const params = { | |
| 60 | + page: 1, | |
| 61 | + row: 100, | |
| 62 | + unitId: unitId, | |
| 63 | + communityId: this.communityId | |
| 64 | + } | |
| 65 | + const { data } = await listCarStructure(params) | |
| 66 | + this.carStructureInfo.cars = data | |
| 67 | + } catch (error) { | |
| 68 | + this.$message.error(this.$t('carStructure.fetchError')) | |
| 69 | + } | |
| 70 | + }, | |
| 71 | + getBgColor(car) { | |
| 72 | + if (!car.ownerName) { | |
| 73 | + return "#1AB394" | |
| 74 | + } | |
| 75 | + if (car.oweAmount > 0) { | |
| 76 | + return "#DC3545" | |
| 77 | + } | |
| 78 | + return "#1296db" | |
| 79 | + }, | |
| 80 | + toSimplifyAcceptance(car) { | |
| 81 | + const date = new Date() | |
| 82 | + this.$store.dispatch('app/saveData', { | |
| 83 | + key: "JAVA110_IS_BACK", | |
| 84 | + value: date.getTime() | |
| 85 | + }) | |
| 86 | + this.$store.dispatch('app/saveData', { | |
| 87 | + key: 'simplifyAcceptanceSearch', | |
| 88 | + value: { | |
| 89 | + searchType: '1', | |
| 90 | + searchValue: `${car.floorNum}-${car.unitNum}-${car.roomNum}`, | |
| 91 | + searchPlaceholder: this.$t('carStructure.searchPlaceholder') | |
| 92 | + } | |
| 93 | + }) | |
| 94 | + this.$router.push('/pages/property/simplifyAcceptance?tab=业务受理') | |
| 95 | + } | |
| 96 | + } | |
| 97 | +} | |
| 98 | +</script> | |
| 99 | + | |
| 100 | +<style lang="scss" scoped> | |
| 101 | +.car-structure-container { | |
| 102 | + padding: 20px; | |
| 103 | + | |
| 104 | + .car-list-card { | |
| 105 | + height: 100%; | |
| 106 | + min-height: calc(100vh - 120px); | |
| 107 | + } | |
| 108 | + | |
| 109 | + .car-card { | |
| 110 | + color: #fff; | |
| 111 | + border-radius: 5px; | |
| 112 | + cursor: pointer; | |
| 113 | + min-height: 120px; | |
| 114 | + display: flex; | |
| 115 | + flex-direction: column; | |
| 116 | + justify-content: center; | |
| 117 | + transition: all 0.3s; | |
| 118 | + | |
| 119 | + &:hover { | |
| 120 | + transform: scale(1.05); | |
| 121 | + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); | |
| 122 | + } | |
| 123 | + } | |
| 124 | + | |
| 125 | + .margin-top { | |
| 126 | + margin-top: 10px; | |
| 127 | + } | |
| 128 | + | |
| 129 | + .margin-bottom { | |
| 130 | + margin-bottom: 20px; | |
| 131 | + } | |
| 132 | + | |
| 133 | + .padding { | |
| 134 | + padding: 10px; | |
| 135 | + } | |
| 136 | + | |
| 137 | + .text-center { | |
| 138 | + text-align: center; | |
| 139 | + } | |
| 140 | +} | |
| 141 | +</style> | |
| 0 | 142 | \ No newline at end of file | ... | ... |
src/views/fee/payFeeQrcodeLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + payFeeQrcode: { | |
| 4 | + search: { | |
| 5 | + title: 'Search Condition', | |
| 6 | + qrcodeName: 'QR Code Name' | |
| 7 | + }, | |
| 8 | + list: { | |
| 9 | + title: 'Payment QR Code List' | |
| 10 | + }, | |
| 11 | + table: { | |
| 12 | + qrcodeName: 'Name', | |
| 13 | + queryWay: 'Query Method', | |
| 14 | + smsValidate: 'Validation', | |
| 15 | + customFee: 'Custom Payment', | |
| 16 | + feeType: 'Display Fee', | |
| 17 | + state: 'Status', | |
| 18 | + createStaffName: 'Creator', | |
| 19 | + createTime: 'Create Time' | |
| 20 | + }, | |
| 21 | + queryWay: { | |
| 22 | + byPhone: 'By Owner Phone', | |
| 23 | + byHouse: 'By House', | |
| 24 | + byBoth: 'By House or Owner Phone' | |
| 25 | + }, | |
| 26 | + feeType: { | |
| 27 | + owner: 'Owner Fee', | |
| 28 | + house: 'House Fee' | |
| 29 | + }, | |
| 30 | + operation: { | |
| 31 | + viewQrcode: 'QR Code' | |
| 32 | + }, | |
| 33 | + form: { | |
| 34 | + qrcodeName: 'Name', | |
| 35 | + queryWay: 'Query Method', | |
| 36 | + smsValidate: 'Validation', | |
| 37 | + customFee: 'Custom Payment', | |
| 38 | + preFee: 'Prepayment', | |
| 39 | + content: 'Prompt Content' | |
| 40 | + }, | |
| 41 | + placeholder: { | |
| 42 | + qrcodeName: 'Please enter name', | |
| 43 | + queryWay: 'Please select query method', | |
| 44 | + smsValidate: 'Please select validation', | |
| 45 | + customFee: 'Please select custom payment', | |
| 46 | + preFee: 'Please select prepayment', | |
| 47 | + content: 'Please enter prompt content' | |
| 48 | + }, | |
| 49 | + rules: { | |
| 50 | + qrcodeName: 'Name cannot be empty', | |
| 51 | + qrcodeNameMax: 'Name cannot exceed 128 characters', | |
| 52 | + queryWay: 'Please select query method', | |
| 53 | + smsValidate: 'Please select validation', | |
| 54 | + customFee: 'Please select custom payment', | |
| 55 | + preFee: 'Please select prepayment', | |
| 56 | + content: 'Prompt content cannot be empty', | |
| 57 | + contentMax: 'Prompt content cannot exceed 512 characters', | |
| 58 | + pfqId: 'ID cannot be empty' | |
| 59 | + }, | |
| 60 | + add: { | |
| 61 | + title: 'Add Payment QR Code' | |
| 62 | + }, | |
| 63 | + edit: { | |
| 64 | + title: 'Edit Payment QR Code' | |
| 65 | + }, | |
| 66 | + delete: { | |
| 67 | + title: 'Delete Confirmation', | |
| 68 | + confirmText: 'Are you sure to delete this payment QR code?' | |
| 69 | + }, | |
| 70 | + view: { | |
| 71 | + title: 'Payment QR Code', | |
| 72 | + tip: 'Please take a screenshot and post it at the cashier' | |
| 73 | + }, | |
| 74 | + message: { | |
| 75 | + addSuccess: 'Added successfully', | |
| 76 | + addFailed: 'Failed to add', | |
| 77 | + editSuccess: 'Modified successfully', | |
| 78 | + editFailed: 'Failed to modify', | |
| 79 | + deleteSuccess: 'Deleted successfully', | |
| 80 | + deleteFailed: 'Failed to delete', | |
| 81 | + fetchError: 'Failed to get data' | |
| 82 | + } | |
| 83 | + } | |
| 84 | + }, | |
| 85 | + zh: { | |
| 86 | + payFeeQrcode: { | |
| 87 | + search: { | |
| 88 | + title: '查询条件', | |
| 89 | + qrcodeName: '二维码名称' | |
| 90 | + }, | |
| 91 | + list: { | |
| 92 | + title: '支付二维码列表' | |
| 93 | + }, | |
| 94 | + table: { | |
| 95 | + qrcodeName: '名称', | |
| 96 | + queryWay: '查询方式', | |
| 97 | + smsValidate: '验证', | |
| 98 | + customFee: '自定义缴费', | |
| 99 | + feeType: '展示费用', | |
| 100 | + state: '状态', | |
| 101 | + createStaffName: '创建人', | |
| 102 | + createTime: '创建时间' | |
| 103 | + }, | |
| 104 | + queryWay: { | |
| 105 | + byPhone: '按业主手机号', | |
| 106 | + byHouse: '按房屋', | |
| 107 | + byBoth: '按房屋或者业主手机号' | |
| 108 | + }, | |
| 109 | + feeType: { | |
| 110 | + owner: '业主费用', | |
| 111 | + house: '房屋费用' | |
| 112 | + }, | |
| 113 | + operation: { | |
| 114 | + viewQrcode: '二维码' | |
| 115 | + }, | |
| 116 | + form: { | |
| 117 | + qrcodeName: '名称', | |
| 118 | + queryWay: '查询方式', | |
| 119 | + smsValidate: '验证', | |
| 120 | + customFee: '自定义缴费', | |
| 121 | + preFee: '预交费', | |
| 122 | + content: '提示内容' | |
| 123 | + }, | |
| 124 | + placeholder: { | |
| 125 | + qrcodeName: '请输入名称', | |
| 126 | + queryWay: '请选择查询方式', | |
| 127 | + smsValidate: '请选择验证', | |
| 128 | + customFee: '请选择自定义缴费', | |
| 129 | + preFee: '请选择预交费', | |
| 130 | + content: '请输入提示内容' | |
| 131 | + }, | |
| 132 | + rules: { | |
| 133 | + qrcodeName: '名称不能为空', | |
| 134 | + qrcodeNameMax: '名称不能超过128个字符', | |
| 135 | + queryWay: '请选择查询方式', | |
| 136 | + smsValidate: '请选择验证', | |
| 137 | + customFee: '请选择自定义缴费', | |
| 138 | + preFee: '请选择预交费', | |
| 139 | + content: '提示内容不能为空', | |
| 140 | + contentMax: '提示内容不能超过512个字符', | |
| 141 | + pfqId: '编号不能为空' | |
| 142 | + }, | |
| 143 | + add: { | |
| 144 | + title: '添加支付二维码' | |
| 145 | + }, | |
| 146 | + edit: { | |
| 147 | + title: '修改支付二维码' | |
| 148 | + }, | |
| 149 | + delete: { | |
| 150 | + title: '删除确认', | |
| 151 | + confirmText: '确定删除该支付二维码吗?' | |
| 152 | + }, | |
| 153 | + view: { | |
| 154 | + title: '支付二维码', | |
| 155 | + tip: '请截图贴在收银台' | |
| 156 | + }, | |
| 157 | + message: { | |
| 158 | + addSuccess: '添加成功', | |
| 159 | + addFailed: '添加失败', | |
| 160 | + editSuccess: '修改成功', | |
| 161 | + editFailed: '修改失败', | |
| 162 | + deleteSuccess: '删除成功', | |
| 163 | + deleteFailed: '删除失败', | |
| 164 | + fetchError: '获取数据失败' | |
| 165 | + } | |
| 166 | + } | |
| 167 | + } | |
| 168 | +} | |
| 0 | 169 | \ No newline at end of file | ... | ... |
src/views/fee/payFeeQrcodeList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="pay-fee-qrcode-container"> | |
| 3 | + <!-- Search Condition --> | |
| 4 | + <el-card class="search-wrapper"> | |
| 5 | + <div slot="header" class="flex justify-between"> | |
| 6 | + <span>{{ $t('payFeeQrcode.search.title') }}</span> | |
| 7 | + </div> | |
| 8 | + <el-row :gutter="20"> | |
| 9 | + <el-col :span="6"> | |
| 10 | + <el-input v-model="searchForm.qrcodeName" :placeholder="$t('payFeeQrcode.search.qrcodeName')" clearable /> | |
| 11 | + </el-col> | |
| 12 | + <el-col :span="6"> | |
| 13 | + <el-button type="primary" @click="handleSearch"> | |
| 14 | + <i class="el-icon-search"></i> | |
| 15 | + {{ $t('common.search') }} | |
| 16 | + </el-button> | |
| 17 | + <el-button @click="handleReset"> | |
| 18 | + <i class="el-icon-refresh"></i> | |
| 19 | + {{ $t('common.reset') }} | |
| 20 | + </el-button> | |
| 21 | + </el-col> | |
| 22 | + </el-row> | |
| 23 | + </el-card> | |
| 24 | + | |
| 25 | + <!-- Data List --> | |
| 26 | + <el-card class="list-wrapper"> | |
| 27 | + <div slot="header" class="flex justify-between"> | |
| 28 | + <span>{{ $t('payFeeQrcode.list.title') }}</span> | |
| 29 | + <el-button type="primary" size="small" class="float-right" @click="handleAdd"> | |
| 30 | + <i class="el-icon-plus"></i> | |
| 31 | + {{ $t('common.add') }} | |
| 32 | + </el-button> | |
| 33 | + </div> | |
| 34 | + | |
| 35 | + <el-table v-loading="loading" :data="tableData" border style="width: 100%"> | |
| 36 | + <el-table-column prop="qrcodeName" :label="$t('payFeeQrcode.table.qrcodeName')" align="center" /> | |
| 37 | + <el-table-column prop="queryWay" :label="$t('payFeeQrcode.table.queryWay')" align="center"> | |
| 38 | + <template slot-scope="scope"> | |
| 39 | + <span v-if="scope.row.queryWay === '1001'"> | |
| 40 | + {{ $t('payFeeQrcode.queryWay.byPhone') }} | |
| 41 | + </span> | |
| 42 | + <span v-else-if="scope.row.queryWay === '2002'"> | |
| 43 | + {{ $t('payFeeQrcode.queryWay.byHouse') }} | |
| 44 | + </span> | |
| 45 | + <span v-else> | |
| 46 | + {{ $t('payFeeQrcode.queryWay.byBoth') }} | |
| 47 | + </span> | |
| 48 | + </template> | |
| 49 | + </el-table-column> | |
| 50 | + <el-table-column prop="smsValidate" :label="$t('payFeeQrcode.table.smsValidate')" align="center"> | |
| 51 | + <template slot-scope="scope"> | |
| 52 | + {{ scope.row.smsValidate === 'ON' ? $t('common.yes') : $t('common.no') }} | |
| 53 | + </template> | |
| 54 | + </el-table-column> | |
| 55 | + <el-table-column prop="customFee" :label="$t('payFeeQrcode.table.customFee')" align="center"> | |
| 56 | + <template slot-scope="scope"> | |
| 57 | + {{ scope.row.customFee === 'ON' ? $t('common.yes') : $t('common.no') }} | |
| 58 | + </template> | |
| 59 | + </el-table-column> | |
| 60 | + <el-table-column prop="feeType" :label="$t('payFeeQrcode.table.feeType')" align="center"> | |
| 61 | + <template slot-scope="scope"> | |
| 62 | + {{ scope.row.feeType === 'OWNER' ? $t('payFeeQrcode.feeType.owner') : $t('payFeeQrcode.feeType.house') }} | |
| 63 | + </template> | |
| 64 | + </el-table-column> | |
| 65 | + <el-table-column prop="state" :label="$t('payFeeQrcode.table.state')" align="center"> | |
| 66 | + <template slot-scope="scope"> | |
| 67 | + <el-tag :type="scope.row.state === 'ON' ? 'success' : 'danger'"> | |
| 68 | + {{ scope.row.state === 'ON' ? $t('common.enabled') : $t('common.disabled') }} | |
| 69 | + </el-tag> | |
| 70 | + </template> | |
| 71 | + </el-table-column> | |
| 72 | + <el-table-column prop="createStaffName" :label="$t('payFeeQrcode.table.createStaffName')" align="center" /> | |
| 73 | + <el-table-column prop="createTime" :label="$t('payFeeQrcode.table.createTime')" align="center" /> | |
| 74 | + <el-table-column :label="$t('common.operation')" align="center" width="250"> | |
| 75 | + <template slot-scope="scope"> | |
| 76 | + <el-button size="mini" @click="handleView(scope.row)"> | |
| 77 | + {{ $t('payFeeQrcode.operation.viewQrcode') }} | |
| 78 | + </el-button> | |
| 79 | + <el-button size="mini" type="primary" @click="handleEdit(scope.row)"> | |
| 80 | + {{ $t('common.edit') }} | |
| 81 | + </el-button> | |
| 82 | + <el-button size="mini" type="danger" @click="handleDelete(scope.row)"> | |
| 83 | + {{ $t('common.delete') }} | |
| 84 | + </el-button> | |
| 85 | + </template> | |
| 86 | + </el-table-column> | |
| 87 | + </el-table> | |
| 88 | + | |
| 89 | + <el-pagination :current-page="pagination.current" :page-sizes="[10, 20, 30, 50]" :page-size="pagination.size" | |
| 90 | + :total="pagination.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" | |
| 91 | + @current-change="handleCurrentChange" /> | |
| 92 | + </el-card> | |
| 93 | + | |
| 94 | + <!-- Components --> | |
| 95 | + <add-pay-fee-qrcode ref="addDialog" @success="handleSuccess" /> | |
| 96 | + <edit-pay-fee-qrcode ref="editDialog" @success="handleSuccess" /> | |
| 97 | + <delete-pay-fee-qrcode ref="deleteDialog" @success="handleSuccess" /> | |
| 98 | + <view-pay-fee-qrcode ref="viewDialog" /> | |
| 99 | + </div> | |
| 100 | +</template> | |
| 101 | + | |
| 102 | +<script> | |
| 103 | +import { listPayFeeQrcode } from '@/api/fee/payFeeQrcodeApi' | |
| 104 | +import AddPayFeeQrcode from '@/components/fee/addPayFeeQrcode' | |
| 105 | +import EditPayFeeQrcode from '@/components/fee/editPayFeeQrcode' | |
| 106 | +import DeletePayFeeQrcode from '@/components/fee/deletePayFeeQrcode' | |
| 107 | +import ViewPayFeeQrcode from '@/components/fee/viewPayFeeQrcode' | |
| 108 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 109 | + | |
| 110 | +export default { | |
| 111 | + name: 'PayFeeQrcodeList', | |
| 112 | + components: { | |
| 113 | + AddPayFeeQrcode, | |
| 114 | + EditPayFeeQrcode, | |
| 115 | + DeletePayFeeQrcode, | |
| 116 | + ViewPayFeeQrcode | |
| 117 | + }, | |
| 118 | + data() { | |
| 119 | + return { | |
| 120 | + loading: false, | |
| 121 | + searchForm: { | |
| 122 | + qrcodeName: '', | |
| 123 | + customFee: '', | |
| 124 | + preFee: '' | |
| 125 | + }, | |
| 126 | + tableData: [], | |
| 127 | + pagination: { | |
| 128 | + current: 1, | |
| 129 | + size: 10, | |
| 130 | + total: 0 | |
| 131 | + }, | |
| 132 | + communityId: '' | |
| 133 | + } | |
| 134 | + }, | |
| 135 | + created() { | |
| 136 | + this.communityId = getCommunityId() | |
| 137 | + this.getList() | |
| 138 | + }, | |
| 139 | + methods: { | |
| 140 | + async getList() { | |
| 141 | + try { | |
| 142 | + this.loading = true | |
| 143 | + const params = { | |
| 144 | + page: this.pagination.current, | |
| 145 | + row: this.pagination.size, | |
| 146 | + qrcodeName: this.searchForm.qrcodeName, | |
| 147 | + communityId: this.communityId | |
| 148 | + } | |
| 149 | + const { data, total } = await listPayFeeQrcode(params) | |
| 150 | + this.tableData = data | |
| 151 | + this.pagination.total = total | |
| 152 | + } catch (error) { | |
| 153 | + this.$message.error(this.$t('payFeeQrcode.fetchError')) | |
| 154 | + } finally { | |
| 155 | + this.loading = false | |
| 156 | + } | |
| 157 | + }, | |
| 158 | + handleSearch() { | |
| 159 | + this.pagination.current = 1 | |
| 160 | + this.getList() | |
| 161 | + }, | |
| 162 | + handleReset() { | |
| 163 | + this.searchForm = { | |
| 164 | + qrcodeName: '', | |
| 165 | + customFee: '', | |
| 166 | + preFee: '' | |
| 167 | + } | |
| 168 | + this.handleSearch() | |
| 169 | + }, | |
| 170 | + handleAdd() { | |
| 171 | + this.$refs.addDialog.open() | |
| 172 | + }, | |
| 173 | + handleEdit(row) { | |
| 174 | + this.$refs.editDialog.open(row) | |
| 175 | + }, | |
| 176 | + handleDelete(row) { | |
| 177 | + this.$refs.deleteDialog.open(row) | |
| 178 | + }, | |
| 179 | + handleView(row) { | |
| 180 | + this.$refs.viewDialog.open(row) | |
| 181 | + }, | |
| 182 | + handleSuccess() { | |
| 183 | + this.getList() | |
| 184 | + }, | |
| 185 | + handleSizeChange(val) { | |
| 186 | + this.pagination.size = val | |
| 187 | + this.getList() | |
| 188 | + }, | |
| 189 | + handleCurrentChange(val) { | |
| 190 | + this.pagination.current = val | |
| 191 | + this.getList() | |
| 192 | + } | |
| 193 | + } | |
| 194 | +} | |
| 195 | +</script> | |
| 196 | + | |
| 197 | +<style lang="scss" scoped> | |
| 198 | +.pay-fee-qrcode-container { | |
| 199 | + padding: 20px; | |
| 200 | + | |
| 201 | + .search-wrapper { | |
| 202 | + margin-bottom: 20px; | |
| 203 | + | |
| 204 | + .el-input { | |
| 205 | + width: 100%; | |
| 206 | + } | |
| 207 | + } | |
| 208 | + | |
| 209 | + .list-wrapper { | |
| 210 | + .float-right { | |
| 211 | + float: right; | |
| 212 | + } | |
| 213 | + } | |
| 214 | + | |
| 215 | + .el-pagination { | |
| 216 | + margin-top: 20px; | |
| 217 | + text-align: right; | |
| 218 | + } | |
| 219 | +} | |
| 220 | +</style> | |
| 0 | 221 | \ No newline at end of file | ... | ... |
src/views/machine/videoControlLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + videoControl: { | |
| 4 | + title: 'Video Control' | |
| 5 | + }, | |
| 6 | + cameraControlVideo: { | |
| 7 | + camera: 'Camera', | |
| 8 | + fourWay: 'Four Way', | |
| 9 | + sixWay: 'Six Way', | |
| 10 | + getVideoUrlError: 'Failed to get video URL' | |
| 11 | + }, | |
| 12 | + selectVideoMachine: { | |
| 13 | + title: 'Select Video Machine', | |
| 14 | + monitorArea: 'Monitor Area', | |
| 15 | + camera: 'Camera', | |
| 16 | + loadAreasError: 'Failed to load monitor areas', | |
| 17 | + loadMachinesError: 'Failed to load monitor machines' | |
| 18 | + } | |
| 19 | + }, | |
| 20 | + zh: { | |
| 21 | + videoControl: { | |
| 22 | + title: '视频控制' | |
| 23 | + }, | |
| 24 | + cameraControlVideo: { | |
| 25 | + camera: '摄像头', | |
| 26 | + fourWay: '四路', | |
| 27 | + sixWay: '六路', | |
| 28 | + getVideoUrlError: '获取视频地址失败' | |
| 29 | + }, | |
| 30 | + selectVideoMachine: { | |
| 31 | + title: '选择视频设备', | |
| 32 | + monitorArea: '监控区域', | |
| 33 | + camera: '摄像头', | |
| 34 | + loadAreasError: '加载监控区域失败', | |
| 35 | + loadMachinesError: '加载监控设备失败' | |
| 36 | + } | |
| 37 | + } | |
| 38 | +} | |
| 0 | 39 | \ No newline at end of file | ... | ... |
src/views/machine/videoControlList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="video-control-container"> | |
| 3 | + <el-card class="box-card"> | |
| 4 | + | |
| 5 | + <el-row :gutter="20"> | |
| 6 | + <el-col :span="24"> | |
| 7 | + <camera-control-video ref="cameraControl" /> | |
| 8 | + </el-col> | |
| 9 | + </el-row> | |
| 10 | + </el-card> | |
| 11 | + </div> | |
| 12 | +</template> | |
| 13 | + | |
| 14 | +<script> | |
| 15 | +import CameraControlVideo from '@/components/machine/cameraControlVideo.vue' | |
| 16 | + | |
| 17 | +export default { | |
| 18 | + name: 'VideoControlList', | |
| 19 | + components: { | |
| 20 | + CameraControlVideo | |
| 21 | + }, | |
| 22 | + data() { | |
| 23 | + return { | |
| 24 | + communityId: '' | |
| 25 | + } | |
| 26 | + }, | |
| 27 | + created() { | |
| 28 | + this.getCommunityId() | |
| 29 | + }, | |
| 30 | + methods: { | |
| 31 | + async getCommunityId() { | |
| 32 | + try { | |
| 33 | + const { getCommunityId } = await import('@/api/community/communityApi') | |
| 34 | + this.communityId = getCommunityId() | |
| 35 | + } catch (error) { | |
| 36 | + console.error('Failed to get communityId:', error) | |
| 37 | + } | |
| 38 | + } | |
| 39 | + } | |
| 40 | +} | |
| 41 | +</script> | |
| 42 | + | |
| 43 | +<style lang="scss" scoped> | |
| 44 | +.video-control-container { | |
| 45 | + padding: 20px; | |
| 46 | + | |
| 47 | + .box-card { | |
| 48 | + margin-bottom: 20px; | |
| 49 | + } | |
| 50 | + | |
| 51 | + .clearfix { | |
| 52 | + font-size: 18px; | |
| 53 | + font-weight: bold; | |
| 54 | + } | |
| 55 | +} | |
| 56 | +</style> | |
| 0 | 57 | \ No newline at end of file | ... | ... |
src/views/room/listPropertyRightRegistrationDetailLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + propertyRightDetail: { | |
| 4 | + title: 'Property Registration Details', | |
| 5 | + table: { | |
| 6 | + id: 'ID', | |
| 7 | + materialType: 'Material Type', | |
| 8 | + isPayment: 'Payment Status', | |
| 9 | + image: 'Images', | |
| 10 | + createTime: 'Create Time', | |
| 11 | + operation: 'Operations' | |
| 12 | + }, | |
| 13 | + edit: { | |
| 14 | + title: 'Edit Property Registration', | |
| 15 | + materialType: 'Material Type', | |
| 16 | + materialTypePlaceholder: 'Required, please enter material type', | |
| 17 | + materialTypeRequired: 'Material type is required', | |
| 18 | + idCardPhoto: 'ID Card Photos', | |
| 19 | + idCardPhotoTip: 'Please upload front and back photos of ID card', | |
| 20 | + houseContract: 'House Purchase Contract', | |
| 21 | + houseContractTip: 'Please upload up to 10 house purchase contract photos', | |
| 22 | + repairFund: 'Repair Fund Payment', | |
| 23 | + repairFundPlaceholder: 'Please select repair fund payment status', | |
| 24 | + repairFundPhoto: 'Repair Fund Proof', | |
| 25 | + deedTax: 'Deed Tax Payment', | |
| 26 | + deedTaxPlaceholder: 'Please select deed tax payment status', | |
| 27 | + deedTaxPhoto: 'Deed Tax Proof' | |
| 28 | + }, | |
| 29 | + fetchError: 'Failed to fetch property registration details' | |
| 30 | + } | |
| 31 | + }, | |
| 32 | + zh: { | |
| 33 | + propertyRightDetail: { | |
| 34 | + title: '产权登记详情', | |
| 35 | + table: { | |
| 36 | + id: 'ID', | |
| 37 | + materialType: '材料类型', | |
| 38 | + isPayment: '是否缴费', | |
| 39 | + image: '图片', | |
| 40 | + createTime: '创建时间', | |
| 41 | + operation: '操作' | |
| 42 | + }, | |
| 43 | + edit: { | |
| 44 | + title: '修改产权登记', | |
| 45 | + materialType: '材料类型', | |
| 46 | + materialTypePlaceholder: '必填,请填写材料类型', | |
| 47 | + materialTypeRequired: '材料类型不能为空', | |
| 48 | + idCardPhoto: '身份证照片', | |
| 49 | + idCardPhotoTip: '请上传正反两张身份证照片', | |
| 50 | + houseContract: '购房合同', | |
| 51 | + houseContractTip: '请上传不超过十张购房合同照片', | |
| 52 | + repairFund: '维修基金缴纳', | |
| 53 | + repairFundPlaceholder: '请选择维修基金缴纳状态', | |
| 54 | + repairFundPhoto: '维修基金证明', | |
| 55 | + deedTax: '契税缴纳', | |
| 56 | + deedTaxPlaceholder: '请选择契税缴纳状态', | |
| 57 | + deedTaxPhoto: '契税证明' | |
| 58 | + }, | |
| 59 | + fetchError: '获取产权登记详情失败' | |
| 60 | + } | |
| 61 | + } | |
| 62 | +} | |
| 0 | 63 | \ No newline at end of file | ... | ... |
src/views/room/listPropertyRightRegistrationDetailList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="property-right-detail-container"> | |
| 3 | + <el-card class="box-card"> | |
| 4 | + <div slot="header" class="clearfix"> | |
| 5 | + <div class="card-header"> | |
| 6 | + <div> | |
| 7 | + <span>{{ listPropertyRightRegistrationDetailInfo.conditions.floorNum }}-{{ | |
| 8 | + listPropertyRightRegistrationDetailInfo.conditions.unitNum }}-{{ | |
| 9 | + listPropertyRightRegistrationDetailInfo.conditions.roomNum }}</span> | |
| 10 | + <span>{{ $t('propertyRightDetail.title') }}</span> | |
| 11 | + </div> | |
| 12 | + <div class="card-tools"> | |
| 13 | + <el-button type="primary" size="small" @click="_goBack"> | |
| 14 | + <i class="el-icon-close"></i> | |
| 15 | + <span>{{ $t('common.back') }}</span> | |
| 16 | + </el-button> | |
| 17 | + </div> | |
| 18 | + </div> | |
| 19 | + </div> | |
| 20 | + | |
| 21 | + <el-table :data="listPropertyRightRegistrationDetailInfo.propertyRightRegistrationDetails" border | |
| 22 | + style="width: 100%" v-loading="loading"> | |
| 23 | + <el-table-column prop="prrdId" :label="$t('propertyRightDetail.table.id')" align="center" /> | |
| 24 | + <el-table-column prop="securitiesName" :label="$t('propertyRightDetail.table.materialType')" align="center" /> | |
| 25 | + <el-table-column :label="$t('propertyRightDetail.table.isPayment')" align="center"> | |
| 26 | + <template slot-scope="scope"> | |
| 27 | + {{ scope.row.isTrue === '-1' ? '--' : scope.row.isTrue === 'true' ? $t('common.yes') : $t('common.no') }} | |
| 28 | + </template> | |
| 29 | + </el-table-column> | |
| 30 | + <el-table-column :label="$t('propertyRightDetail.table.image')" align="center"> | |
| 31 | + <template slot-scope="scope"> | |
| 32 | + <div v-if="scope.row.securities === '001'"> | |
| 33 | + <div v-for="(item, index) in scope.row.idCardUrlShow" :key="index" class="image-container" | |
| 34 | + @click="showImg(item)"> | |
| 35 | + <el-image :src="item" :preview-src-list="scope.row.idCardUrlShow" fit="cover" | |
| 36 | + style="width: 50px; height: 50px"> | |
| 37 | + <div slot="error" class="image-slot"> | |
| 38 | + <img src="/img/noPhoto.jpg" style="width: 50px; height: 50px"> | |
| 39 | + </div> | |
| 40 | + </el-image> | |
| 41 | + <img src="/img/icon-bigimg.png" class="preview-icon"> | |
| 42 | + </div> | |
| 43 | + </div> | |
| 44 | + <div v-else-if="scope.row.securities === '002'"> | |
| 45 | + <div v-for="(item, index) in scope.row.housePurchaseUrlShow" :key="index" class="image-container" | |
| 46 | + @click="showImg(item)"> | |
| 47 | + <el-image :src="item" :preview-src-list="scope.row.housePurchaseUrlShow" fit="cover" | |
| 48 | + style="width: 50px; height: 50px"> | |
| 49 | + <div slot="error" class="image-slot"> | |
| 50 | + <img src="/img/noPhoto.jpg" style="width: 50px; height: 50px"> | |
| 51 | + </div> | |
| 52 | + </el-image> | |
| 53 | + <img src="/img/icon-bigimg.png" class="preview-icon"> | |
| 54 | + </div> | |
| 55 | + </div> | |
| 56 | + <div v-else-if="scope.row.securities === '003'"> | |
| 57 | + <div v-for="(item, index) in scope.row.repairUrlShow" :key="index" class="image-container" | |
| 58 | + @click="showImg(item)"> | |
| 59 | + <el-image :src="item" :preview-src-list="scope.row.repairUrlShow" fit="cover" | |
| 60 | + style="width: 50px; height: 50px"> | |
| 61 | + <div slot="error" class="image-slot"> | |
| 62 | + <img src="/img/noPhoto.jpg" style="width: 50px; height: 50px"> | |
| 63 | + </div> | |
| 64 | + </el-image> | |
| 65 | + <img src="/img/icon-bigimg.png" class="preview-icon"> | |
| 66 | + </div> | |
| 67 | + </div> | |
| 68 | + <div v-else-if="scope.row.securities === '004'"> | |
| 69 | + <div v-for="(item, index) in scope.row.deedTaxUrlShow" :key="index" class="image-container" | |
| 70 | + @click="showImg(item)"> | |
| 71 | + <el-image :src="item" :preview-src-list="scope.row.deedTaxUrlShow" fit="cover" | |
| 72 | + style="width: 50px; height: 50px"> | |
| 73 | + <div slot="error" class="image-slot"> | |
| 74 | + <img src="/img/noPhoto.jpg" style="width: 50px; height: 50px"> | |
| 75 | + </div> | |
| 76 | + </el-image> | |
| 77 | + <img src="/img/icon-bigimg.png" class="preview-icon"> | |
| 78 | + </div> | |
| 79 | + </div> | |
| 80 | + </template> | |
| 81 | + </el-table-column> | |
| 82 | + <el-table-column prop="createTime" :label="$t('propertyRightDetail.table.createTime')" align="center" /> | |
| 83 | + <el-table-column :label="$t('common.operation')" align="center" width="150"> | |
| 84 | + <template slot-scope="scope"> | |
| 85 | + <el-button size="mini" type="primary" @click="_openEditPropertyRightRegistrationDetailModel(scope.row)"> | |
| 86 | + {{ $t('common.edit') }} | |
| 87 | + </el-button> | |
| 88 | + </template> | |
| 89 | + </el-table-column> | |
| 90 | + </el-table> | |
| 91 | + | |
| 92 | + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size" | |
| 93 | + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" | |
| 94 | + @current-change="handleCurrentChange" /> | |
| 95 | + </el-card> | |
| 96 | + | |
| 97 | + <edit-property-right-registration-detail ref="editPropertyRightRegistrationDetail" @success="handleSuccess" /> | |
| 98 | + <view-image ref="viewImage" /> | |
| 99 | + </div> | |
| 100 | +</template> | |
| 101 | + | |
| 102 | +<script> | |
| 103 | +import { listPropertyRightRegistrationDetail } from '@/api/room/listPropertyRightRegistrationDetailApi' | |
| 104 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 105 | +import EditPropertyRightRegistrationDetail from '@/components/room/editPropertyRightRegistrationDetail' | |
| 106 | +import ViewImage from '@/components/system/viewImage' | |
| 107 | + | |
| 108 | +export default { | |
| 109 | + name: 'ListPropertyRightRegistrationDetail', | |
| 110 | + components: { | |
| 111 | + EditPropertyRightRegistrationDetail, | |
| 112 | + ViewImage | |
| 113 | + }, | |
| 114 | + data() { | |
| 115 | + return { | |
| 116 | + loading: false, | |
| 117 | + listPropertyRightRegistrationDetailInfo: { | |
| 118 | + propertyRightRegistrationDetails: [], | |
| 119 | + conditions: { | |
| 120 | + prrId: '', | |
| 121 | + securities: '', | |
| 122 | + floorNum: '', | |
| 123 | + unitNum: '', | |
| 124 | + roomNum: '', | |
| 125 | + isTrue: '', | |
| 126 | + communityId: '' | |
| 127 | + } | |
| 128 | + }, | |
| 129 | + page: { | |
| 130 | + current: 1, | |
| 131 | + size: 10, | |
| 132 | + total: 0 | |
| 133 | + } | |
| 134 | + } | |
| 135 | + }, | |
| 136 | + created() { | |
| 137 | + this.listPropertyRightRegistrationDetailInfo.conditions.communityId = getCommunityId() | |
| 138 | + this.listPropertyRightRegistrationDetailInfo.conditions.prrId = this.$route.query.prrId | |
| 139 | + this.listPropertyRightRegistrationDetailInfo.conditions.floorNum = this.$route.query.floorNum | |
| 140 | + this.listPropertyRightRegistrationDetailInfo.conditions.unitNum = this.$route.query.unitNum | |
| 141 | + this.listPropertyRightRegistrationDetailInfo.conditions.roomNum = this.$route.query.roomNum | |
| 142 | + this.getList() | |
| 143 | + }, | |
| 144 | + methods: { | |
| 145 | + async getList() { | |
| 146 | + try { | |
| 147 | + this.loading = true | |
| 148 | + const params = { | |
| 149 | + page: this.page.current, | |
| 150 | + row: this.page.size, | |
| 151 | + ...this.listPropertyRightRegistrationDetailInfo.conditions | |
| 152 | + } | |
| 153 | + const { data, total } = await listPropertyRightRegistrationDetail(params) | |
| 154 | + this.listPropertyRightRegistrationDetailInfo.propertyRightRegistrationDetails = data.map(item => { | |
| 155 | + if (item.securities === '001' && item.idCardUrl) { | |
| 156 | + item.idCardUrl = item.idCardUrl.split(',') | |
| 157 | + item.idCardUrlShow = item.idCardUrl.map(url => `/callComponent/download/getFile/file?fileId=${url}&communityId=-1&time=${new Date()}`) | |
| 158 | + } | |
| 159 | + if (item.securities === '002' && item.housePurchaseUrl) { | |
| 160 | + item.housePurchaseUrl = item.housePurchaseUrl.split(',') | |
| 161 | + item.housePurchaseUrlShow = item.housePurchaseUrl.map(url => `/callComponent/download/getFile/file?fileId=${url}&communityId=-1&time=${new Date()}`) | |
| 162 | + } | |
| 163 | + if (item.securities === '003' && item.repairUrl) { | |
| 164 | + item.repairUrl = item.repairUrl.split(',') | |
| 165 | + item.repairUrlShow = item.repairUrl.map(url => `/callComponent/download/getFile/file?fileId=${url}&communityId=-1&time=${new Date()}`) | |
| 166 | + } | |
| 167 | + if (item.securities === '004' && item.deedTaxUrl) { | |
| 168 | + item.deedTaxUrl = item.deedTaxUrl.split(',') | |
| 169 | + item.deedTaxUrlShow = item.deedTaxUrl.map(url => `/callComponent/download/getFile/file?fileId=${url}&communityId=-1&time=${new Date()}`) | |
| 170 | + } | |
| 171 | + return item | |
| 172 | + }) | |
| 173 | + this.page.total = total | |
| 174 | + } catch (error) { | |
| 175 | + this.$message.error(this.$t('propertyRightDetail.fetchError')) | |
| 176 | + } finally { | |
| 177 | + this.loading = false | |
| 178 | + } | |
| 179 | + }, | |
| 180 | + handleSizeChange(val) { | |
| 181 | + this.page.size = val | |
| 182 | + this.getList() | |
| 183 | + }, | |
| 184 | + handleCurrentChange(val) { | |
| 185 | + this.page.current = val | |
| 186 | + this.getList() | |
| 187 | + }, | |
| 188 | + _openEditPropertyRightRegistrationDetailModel(row) { | |
| 189 | + this.$refs.editPropertyRightRegistrationDetail.open(row) | |
| 190 | + }, | |
| 191 | + showImg(url) { | |
| 192 | + this.$refs.viewImage.open(url) | |
| 193 | + }, | |
| 194 | + _goBack() { | |
| 195 | + this.$router.go(-1) | |
| 196 | + }, | |
| 197 | + handleSuccess() { | |
| 198 | + this.getList() | |
| 199 | + } | |
| 200 | + } | |
| 201 | +} | |
| 202 | +</script> | |
| 203 | + | |
| 204 | +<style lang="scss" scoped> | |
| 205 | +.property-right-detail-container { | |
| 206 | + padding: 20px; | |
| 207 | + | |
| 208 | + .box-card { | |
| 209 | + margin-bottom: 20px; | |
| 210 | + | |
| 211 | + .card-header { | |
| 212 | + display: flex; | |
| 213 | + align-items: center; | |
| 214 | + justify-content: space-between; | |
| 215 | + | |
| 216 | + span { | |
| 217 | + font-size: 18px; | |
| 218 | + font-weight: bold; | |
| 219 | + } | |
| 220 | + } | |
| 221 | + } | |
| 222 | + | |
| 223 | + .image-container { | |
| 224 | + position: relative; | |
| 225 | + display: inline-block; | |
| 226 | + margin-right: 10px; | |
| 227 | + | |
| 228 | + .preview-icon { | |
| 229 | + position: absolute; | |
| 230 | + right: 0; | |
| 231 | + bottom: 0; | |
| 232 | + width: 20px; | |
| 233 | + height: 20px; | |
| 234 | + } | |
| 235 | + } | |
| 236 | + | |
| 237 | + .el-pagination { | |
| 238 | + margin-top: 20px; | |
| 239 | + text-align: right; | |
| 240 | + } | |
| 241 | +} | |
| 242 | +</style> | |
| 0 | 243 | \ No newline at end of file | ... | ... |
src/views/room/propertyRightRegistrationManageLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + zh: { | |
| 3 | + propertyRightRegistration: { | |
| 4 | + search: { | |
| 5 | + title: '查询条件', | |
| 6 | + roomId: '房屋ID', | |
| 7 | + allNum: '房屋编号(楼栋-单元-房屋)', | |
| 8 | + name: '姓名', | |
| 9 | + link: '联系方式', | |
| 10 | + idCard: '身份证号', | |
| 11 | + address: '地址', | |
| 12 | + state: '审核状态', | |
| 13 | + floor: '楼栋', | |
| 14 | + unit: '单元' | |
| 15 | + }, | |
| 16 | + list: { | |
| 17 | + title: '房屋产权' | |
| 18 | + }, | |
| 19 | + table: { | |
| 20 | + prrId: '房屋产权ID', | |
| 21 | + roomId: '房屋ID', | |
| 22 | + roomNum: '房屋编号', | |
| 23 | + name: '姓名', | |
| 24 | + link: '联系方式', | |
| 25 | + idCard: '身份证号', | |
| 26 | + address: '地址', | |
| 27 | + state: '状态' | |
| 28 | + }, | |
| 29 | + add: { | |
| 30 | + title: '添加产权登记', | |
| 31 | + floor: '楼栋', | |
| 32 | + floorRequired: '请选择楼栋', | |
| 33 | + floorPlaceholder: '请选择楼栋', | |
| 34 | + floorUnit: '号楼', | |
| 35 | + unit: '单元', | |
| 36 | + unitRequired: '请选择单元', | |
| 37 | + unitPlaceholder: '请选择单元', | |
| 38 | + unitUnit: '单元', | |
| 39 | + room: '房屋', | |
| 40 | + roomRequired: '请选择房屋', | |
| 41 | + roomPlaceholder: '请选择房屋', | |
| 42 | + name: '姓名', | |
| 43 | + nameRequired: '请输入姓名', | |
| 44 | + namePlaceholder: '请输入姓名', | |
| 45 | + nameLength: '姓名长度在2到64个字符之间', | |
| 46 | + link: '联系方式', | |
| 47 | + linkRequired: '请输入联系方式', | |
| 48 | + linkPlaceholder: '请输入联系方式', | |
| 49 | + idCard: '身份证号', | |
| 50 | + idCardRequired: '请输入身份证号', | |
| 51 | + idCardPlaceholder: '请输入身份证号', | |
| 52 | + idCardFormatError: '身份证格式不正确', | |
| 53 | + address: '地址', | |
| 54 | + addressRequired: '请输入地址', | |
| 55 | + addressPlaceholder: '请输入地址', | |
| 56 | + addressMax: '地址长度不能超过255个字符', | |
| 57 | + idCardPhotos: '身份证照片', | |
| 58 | + idCardPhotosRequired: '请上传身份证照片', | |
| 59 | + idCardPhotosTip: '*请上传正反两张身份证照片*', | |
| 60 | + housePurchasePhotos: '购房合同照片', | |
| 61 | + housePurchasePhotosRequired: '请上传购房合同照片', | |
| 62 | + housePurchasePhotosTip: '*请上传不超过十张购房合同图片*', | |
| 63 | + isTrue: '维修基金是否缴纳', | |
| 64 | + isTrueRequired: '请选择维修基金是否缴纳', | |
| 65 | + isTruePlaceholder: '请选择维修基金是否缴纳', | |
| 66 | + repairPhotos: '维修基金照片', | |
| 67 | + repairPhotosRequired: '请上传维修基金照片', | |
| 68 | + flag: '契税是否缴纳', | |
| 69 | + flagRequired: '请选择契税是否缴纳', | |
| 70 | + flagPlaceholder: '请选择契税是否缴纳', | |
| 71 | + deedTaxPhotos: '契税证明照片', | |
| 72 | + deedTaxPhotosRequired: '请上传契税证明照片', | |
| 73 | + success: '添加成功', | |
| 74 | + error: '添加失败' | |
| 75 | + }, | |
| 76 | + edit: { | |
| 77 | + title: '修改产权登记', | |
| 78 | + floor: '楼栋', | |
| 79 | + floorRequired: '请选择楼栋', | |
| 80 | + floorPlaceholder: '请选择楼栋', | |
| 81 | + floorUnit: '号楼', | |
| 82 | + unit: '单元', | |
| 83 | + unitRequired: '请选择单元', | |
| 84 | + unitPlaceholder: '请选择单元', | |
| 85 | + unitUnit: '单元', | |
| 86 | + room: '房屋', | |
| 87 | + roomRequired: '请选择房屋', | |
| 88 | + roomPlaceholder: '请选择房屋', | |
| 89 | + name: '姓名', | |
| 90 | + nameRequired: '请输入姓名', | |
| 91 | + namePlaceholder: '请输入姓名', | |
| 92 | + nameLength: '姓名长度在2到64个字符之间', | |
| 93 | + link: '联系方式', | |
| 94 | + linkRequired: '请输入联系方式', | |
| 95 | + linkPlaceholder: '请输入联系方式', | |
| 96 | + idCard: '身份证号', | |
| 97 | + idCardRequired: '请输入身份证号', | |
| 98 | + idCardPlaceholder: '请输入身份证号', | |
| 99 | + idCardFormatError: '身份证格式不正确', | |
| 100 | + address: '地址', | |
| 101 | + addressRequired: '请输入地址', | |
| 102 | + addressPlaceholder: '请输入地址', | |
| 103 | + addressMax: '地址长度不能超过255个字符', | |
| 104 | + success: '修改成功', | |
| 105 | + error: '修改失败' | |
| 106 | + }, | |
| 107 | + examine: { | |
| 108 | + title: '产权登记审核', | |
| 109 | + room: '房屋', | |
| 110 | + roomPlaceholder: '房屋信息', | |
| 111 | + state: '状态', | |
| 112 | + stateRequired: '请选择状态', | |
| 113 | + statePlaceholder: '请选择状态', | |
| 114 | + remark: '审核意见', | |
| 115 | + remarkPlaceholder: '请输入审核意见', | |
| 116 | + success: '审核成功', | |
| 117 | + error: '审核失败', | |
| 118 | + fetchStateError: '获取审核状态失败' | |
| 119 | + }, | |
| 120 | + delete: { | |
| 121 | + title: '删除确认', | |
| 122 | + confirm: '确定删除该房屋产权登记信息吗?', | |
| 123 | + tip: '删除后将无法恢复,请谨慎操作!', | |
| 124 | + success: '删除成功', | |
| 125 | + error: '删除失败' | |
| 126 | + }, | |
| 127 | + fetchError: '获取数据失败' | |
| 128 | + }, | |
| 129 | + }, | |
| 130 | + en: { | |
| 131 | + propertyRightRegistration: { | |
| 132 | + search: { | |
| 133 | + title: 'Search Conditions', | |
| 134 | + roomId: 'Room ID', | |
| 135 | + allNum: 'Room Number(Building-Unit-Room)', | |
| 136 | + name: 'Name', | |
| 137 | + link: 'Contact', | |
| 138 | + idCard: 'ID Card', | |
| 139 | + address: 'Address', | |
| 140 | + state: 'Audit Status', | |
| 141 | + floor: 'Building', | |
| 142 | + unit: 'Unit' | |
| 143 | + }, | |
| 144 | + list: { | |
| 145 | + title: 'Property Right' | |
| 146 | + }, | |
| 147 | + table: { | |
| 148 | + prrId: 'Property Right ID', | |
| 149 | + roomId: 'Room ID', | |
| 150 | + roomNum: 'Room Number', | |
| 151 | + name: 'Name', | |
| 152 | + link: 'Contact', | |
| 153 | + idCard: 'ID Card', | |
| 154 | + address: 'Address', | |
| 155 | + state: 'Status' | |
| 156 | + }, | |
| 157 | + add: { | |
| 158 | + title: 'Add Property Registration', | |
| 159 | + floor: 'Building', | |
| 160 | + floorRequired: 'Please select building', | |
| 161 | + floorPlaceholder: 'Please select building', | |
| 162 | + floorUnit: 'Building', | |
| 163 | + unit: 'Unit', | |
| 164 | + unitRequired: 'Please select unit', | |
| 165 | + unitPlaceholder: 'Please select unit', | |
| 166 | + unitUnit: 'Unit', | |
| 167 | + room: 'Room', | |
| 168 | + roomRequired: 'Please select room', | |
| 169 | + roomPlaceholder: 'Please select room', | |
| 170 | + name: 'Name', | |
| 171 | + nameRequired: 'Please enter name', | |
| 172 | + namePlaceholder: 'Please enter name', | |
| 173 | + nameLength: 'Name length should be between 2 and 64 characters', | |
| 174 | + link: 'Contact', | |
| 175 | + linkRequired: 'Please enter contact', | |
| 176 | + linkPlaceholder: 'Please enter contact', | |
| 177 | + idCard: 'ID Card', | |
| 178 | + idCardRequired: 'Please enter ID card', | |
| 179 | + idCardPlaceholder: 'Please enter ID card', | |
| 180 | + idCardFormatError: 'Invalid ID card format', | |
| 181 | + address: 'Address', | |
| 182 | + addressRequired: 'Please enter address', | |
| 183 | + addressPlaceholder: 'Please enter address', | |
| 184 | + addressMax: 'Address length cannot exceed 255 characters', | |
| 185 | + idCardPhotos: 'ID Card Photos', | |
| 186 | + idCardPhotosRequired: 'Please upload ID card photos', | |
| 187 | + idCardPhotosTip: '*Please upload front and back photos of ID card*', | |
| 188 | + housePurchasePhotos: 'House Purchase Contract Photos', | |
| 189 | + housePurchasePhotosRequired: 'Please upload house purchase contract photos', | |
| 190 | + housePurchasePhotosTip: '*Please upload up to ten house purchase contract photos*', | |
| 191 | + isTrue: 'Maintenance Fund Paid', | |
| 192 | + isTrueRequired: 'Please select whether maintenance fund is paid', | |
| 193 | + isTruePlaceholder: 'Please select whether maintenance fund is paid', | |
| 194 | + repairPhotos: 'Maintenance Fund Photos', | |
| 195 | + repairPhotosRequired: 'Please upload maintenance fund photos', | |
| 196 | + flag: 'Deed Tax Paid', | |
| 197 | + flagRequired: 'Please select whether deed tax is paid', | |
| 198 | + flagPlaceholder: 'Please select whether deed tax is paid', | |
| 199 | + deedTaxPhotos: 'Deed Tax Certificate Photos', | |
| 200 | + deedTaxPhotosRequired: 'Please upload deed tax certificate photos', | |
| 201 | + success: 'Added successfully', | |
| 202 | + error: 'Failed to add' | |
| 203 | + }, | |
| 204 | + edit: { | |
| 205 | + title: 'Edit Property Registration', | |
| 206 | + floor: 'Building', | |
| 207 | + floorRequired: 'Please select building', | |
| 208 | + floorPlaceholder: 'Please select building', | |
| 209 | + floorUnit: 'Building', | |
| 210 | + unit: 'Unit', | |
| 211 | + unitRequired: 'Please select unit', | |
| 212 | + unitPlaceholder: 'Please select unit', | |
| 213 | + unitUnit: 'Unit', | |
| 214 | + room: 'Room', | |
| 215 | + roomRequired: 'Please select room', | |
| 216 | + roomPlaceholder: 'Please select room', | |
| 217 | + name: 'Name', | |
| 218 | + nameRequired: 'Please enter name', | |
| 219 | + namePlaceholder: 'Please enter name', | |
| 220 | + nameLength: 'Name length should be between 2 and 64 characters', | |
| 221 | + link: 'Contact', | |
| 222 | + linkRequired: 'Please enter contact', | |
| 223 | + linkPlaceholder: 'Please enter contact', | |
| 224 | + idCard: 'ID Card', | |
| 225 | + idCardRequired: 'Please enter ID card', | |
| 226 | + idCardPlaceholder: 'Please enter ID card', | |
| 227 | + idCardFormatError: 'Invalid ID card format', | |
| 228 | + address: 'Address', | |
| 229 | + addressRequired: 'Please enter address', | |
| 230 | + addressPlaceholder: 'Please enter address', | |
| 231 | + addressMax: 'Address length cannot exceed 255 characters', | |
| 232 | + success: 'Updated successfully', | |
| 233 | + error: 'Failed to update' | |
| 234 | + }, | |
| 235 | + examine: { | |
| 236 | + title: 'Property Registration Audit', | |
| 237 | + room: 'Room', | |
| 238 | + roomPlaceholder: 'Room information', | |
| 239 | + state: 'Status', | |
| 240 | + stateRequired: 'Please select status', | |
| 241 | + statePlaceholder: 'Please select status', | |
| 242 | + remark: 'Audit Opinion', | |
| 243 | + remarkPlaceholder: 'Please enter audit opinion', | |
| 244 | + success: 'Audit successful', | |
| 245 | + error: 'Audit failed', | |
| 246 | + fetchStateError: 'Failed to get audit status' | |
| 247 | + }, | |
| 248 | + delete: { | |
| 249 | + title: 'Delete Confirmation', | |
| 250 | + confirm: 'Are you sure to delete this property registration?', | |
| 251 | + tip: 'The data cannot be recovered after deletion, please operate carefully!', | |
| 252 | + success: 'Deleted successfully', | |
| 253 | + error: 'Failed to delete' | |
| 254 | + }, | |
| 255 | + fetchError: 'Failed to fetch data' | |
| 256 | + } | |
| 257 | + } | |
| 258 | + | |
| 259 | +} | |
| 0 | 260 | \ No newline at end of file | ... | ... |
src/views/room/propertyRightRegistrationManageList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="property-right-registration-container"> | |
| 3 | + <!-- 查询条件 --> | |
| 4 | + <el-card class="search-wrapper"> | |
| 5 | + <div slot="header" class="flex justify-between"> | |
| 6 | + <span>{{ $t('propertyRightRegistration.search.title') }}</span> | |
| 7 | + <el-button type="text" style="float: right; padding: 3px 0" @click="toggleMoreCondition"> | |
| 8 | + {{ showMoreCondition ? $t('common.hide') : $t('common.more') }} | |
| 9 | + </el-button> | |
| 10 | + </div> | |
| 11 | + <el-row :gutter="20"> | |
| 12 | + <el-col :span="6"> | |
| 13 | + <el-input v-model="searchForm.roomId" :placeholder="$t('propertyRightRegistration.search.roomId')" | |
| 14 | + clearable /> | |
| 15 | + </el-col> | |
| 16 | + <el-col :span="8"> | |
| 17 | + <el-input v-model="searchForm.allNum" :placeholder="$t('propertyRightRegistration.search.allNum')" | |
| 18 | + clearable /> | |
| 19 | + </el-col> | |
| 20 | + <el-col :span="6"> | |
| 21 | + <el-input v-model="searchForm.name" :placeholder="$t('propertyRightRegistration.search.name')" clearable /> | |
| 22 | + </el-col> | |
| 23 | + <el-col :span="4"> | |
| 24 | + <el-button type="primary" @click="handleSearch"> | |
| 25 | + {{ $t('common.search') }} | |
| 26 | + </el-button> | |
| 27 | + <el-button @click="handleReset"> | |
| 28 | + {{ $t('common.reset') }} | |
| 29 | + </el-button> | |
| 30 | + </el-col> | |
| 31 | + </el-row> | |
| 32 | + | |
| 33 | + <!-- 更多查询条件 --> | |
| 34 | + <div v-show="showMoreCondition"> | |
| 35 | + <el-row :gutter="20" class="mt-20"> | |
| 36 | + <el-col :span="6"> | |
| 37 | + <el-input v-model="searchForm.link" :placeholder="$t('propertyRightRegistration.search.link')" clearable /> | |
| 38 | + </el-col> | |
| 39 | + <el-col :span="8"> | |
| 40 | + <el-input v-model="searchForm.idCard" :placeholder="$t('propertyRightRegistration.search.idCard')" | |
| 41 | + clearable /> | |
| 42 | + </el-col> | |
| 43 | + <el-col :span="6"> | |
| 44 | + <el-input v-model="searchForm.address" :placeholder="$t('propertyRightRegistration.search.address')" | |
| 45 | + clearable /> | |
| 46 | + </el-col> | |
| 47 | + </el-row> | |
| 48 | + | |
| 49 | + <el-row :gutter="20" class="mt-20"> | |
| 50 | + <el-col :span="6"> | |
| 51 | + <el-select v-model="searchForm.state" :placeholder="$t('propertyRightRegistration.search.state')" | |
| 52 | + style="width:100%"> | |
| 53 | + <el-option v-for="item in states" :key="item.statusCd" :label="item.name" :value="item.statusCd" /> | |
| 54 | + </el-select> | |
| 55 | + </el-col> | |
| 56 | + <el-col :span="8"> | |
| 57 | + <el-select v-model="searchForm.floorId" :placeholder="$t('propertyRightRegistration.search.floor')" | |
| 58 | + style="width:100%" @change="handleFloorChange"> | |
| 59 | + <el-option v-for="item in floors" :key="item.floorId" :label="item.floorName" :value="item.floorId" /> | |
| 60 | + </el-select> | |
| 61 | + </el-col> | |
| 62 | + <el-col :span="6"> | |
| 63 | + <el-select v-model="searchForm.unitId" :placeholder="$t('propertyRightRegistration.search.unit')" | |
| 64 | + style="width:100%"> | |
| 65 | + <el-option v-for="item in units" :key="item.unitId" | |
| 66 | + :label="`${item.unitNum}${$t('propertyRightRegistration.unit')}`" :value="item.unitId" /> | |
| 67 | + </el-select> | |
| 68 | + </el-col> | |
| 69 | + </el-row> | |
| 70 | + </div> | |
| 71 | + </el-card> | |
| 72 | + | |
| 73 | + <!-- 数据列表 --> | |
| 74 | + <el-card class="list-wrapper"> | |
| 75 | + <div slot="header" class="flex justify-between"> | |
| 76 | + <span>{{ $t('propertyRightRegistration.list.title') }}</span> | |
| 77 | + <el-button type="primary" size="small" style="float: right" @click="handleAdd"> | |
| 78 | + <i class="el-icon-plus" /> | |
| 79 | + {{ $t('common.add') }} | |
| 80 | + </el-button> | |
| 81 | + </div> | |
| 82 | + | |
| 83 | + <el-table v-loading="loading" :data="tableData" border style="width: 100%"> | |
| 84 | + <el-table-column prop="prrId" :label="$t('propertyRightRegistration.table.prrId')" align="center" /> | |
| 85 | + <el-table-column prop="roomId" :label="$t('propertyRightRegistration.table.roomId')" align="center" /> | |
| 86 | + <el-table-column :label="$t('propertyRightRegistration.table.roomNum')" align="center"> | |
| 87 | + <template slot-scope="scope"> | |
| 88 | + {{ `${scope.row.floorNum}-${scope.row.unitNum}-${scope.row.roomNum}` }} | |
| 89 | + </template> | |
| 90 | + </el-table-column> | |
| 91 | + <el-table-column prop="name" :label="$t('propertyRightRegistration.table.name')" align="center" /> | |
| 92 | + <el-table-column prop="link" :label="$t('propertyRightRegistration.table.link')" align="center" /> | |
| 93 | + <el-table-column prop="idCard" :label="$t('propertyRightRegistration.table.idCard')" align="center" /> | |
| 94 | + <el-table-column prop="address" :label="$t('propertyRightRegistration.table.address')" align="center" /> | |
| 95 | + <el-table-column prop="stateName" :label="$t('propertyRightRegistration.table.state')" align="center" /> | |
| 96 | + <el-table-column :label="$t('common.operation')" align="center" width="300"> | |
| 97 | + <template slot-scope="scope"> | |
| 98 | + <el-button v-if="scope.row.state !== '1'" size="mini" @click="handleExamine(scope.row)"> | |
| 99 | + {{ $t('common.examine') }} | |
| 100 | + </el-button> | |
| 101 | + <el-button size="mini" type="primary" @click="handleEdit(scope.row)"> | |
| 102 | + {{ $t('common.edit') }} | |
| 103 | + </el-button> | |
| 104 | + <el-button size="mini" type="info" @click="handleDetail(scope.row)"> | |
| 105 | + {{ $t('common.detail') }} | |
| 106 | + </el-button> | |
| 107 | + <el-button size="mini" type="danger" @click="handleDelete(scope.row)"> | |
| 108 | + {{ $t('common.delete') }} | |
| 109 | + </el-button> | |
| 110 | + </template> | |
| 111 | + </el-table-column> | |
| 112 | + </el-table> | |
| 113 | + | |
| 114 | + <el-pagination :current-page="pagination.current" :page-sizes="[10, 20, 30, 50]" :page-size="pagination.size" | |
| 115 | + :total="pagination.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" | |
| 116 | + @current-change="handleCurrentChange" /> | |
| 117 | + </el-card> | |
| 118 | + | |
| 119 | + <!-- 子组件 --> | |
| 120 | + <add-property-right-registration ref="addForm" @success="handleSuccess" /> | |
| 121 | + <examine-property-right-registration ref="examineForm" @success="handleSuccess" /> | |
| 122 | + <edit-property-right-registration ref="editForm" @success="handleSuccess" /> | |
| 123 | + <delete-property-right-registration ref="deleteForm" @success="handleSuccess" /> | |
| 124 | + </div> | |
| 125 | +</template> | |
| 126 | + | |
| 127 | +<script> | |
| 128 | +import { getDict } from '@/api/community/communityApi' | |
| 129 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 130 | +import { listPropertyRightRegistration } from '@/api/room/propertyRightRegistrationManageApi' | |
| 131 | +import { queryFloors, queryUnits } from '@/api/room/roomApi' | |
| 132 | +import AddPropertyRightRegistration from '@/components/room/addPropertyRightRegistration' | |
| 133 | +import ExaminePropertyRightRegistration from '@/components/room/examinePropertyRightRegistration' | |
| 134 | +import EditPropertyRightRegistration from '@/components/room/editPropertyRightRegistration' | |
| 135 | +import DeletePropertyRightRegistration from '@/components/room/deletePropertyRightRegistration' | |
| 136 | + | |
| 137 | +export default { | |
| 138 | + name: 'PropertyRightRegistrationManageList', | |
| 139 | + components: { | |
| 140 | + AddPropertyRightRegistration, | |
| 141 | + ExaminePropertyRightRegistration, | |
| 142 | + EditPropertyRightRegistration, | |
| 143 | + DeletePropertyRightRegistration | |
| 144 | + }, | |
| 145 | + data() { | |
| 146 | + return { | |
| 147 | + loading: false, | |
| 148 | + showMoreCondition: false, | |
| 149 | + searchForm: { | |
| 150 | + roomId: '', | |
| 151 | + allNum: '', | |
| 152 | + name: '', | |
| 153 | + link: '', | |
| 154 | + idCard: '', | |
| 155 | + address: '', | |
| 156 | + state: '', | |
| 157 | + floorId: '', | |
| 158 | + unitId: '', | |
| 159 | + communityId: '' | |
| 160 | + }, | |
| 161 | + tableData: [], | |
| 162 | + pagination: { | |
| 163 | + current: 1, | |
| 164 | + size: 10, | |
| 165 | + total: 0 | |
| 166 | + }, | |
| 167 | + states: [], | |
| 168 | + floors: [], | |
| 169 | + units: [] | |
| 170 | + } | |
| 171 | + }, | |
| 172 | + created() { | |
| 173 | + this.searchForm.communityId = getCommunityId() | |
| 174 | + this.getList() | |
| 175 | + this.getDictData() | |
| 176 | + this.getFloors() | |
| 177 | + }, | |
| 178 | + methods: { | |
| 179 | + async getList() { | |
| 180 | + try { | |
| 181 | + this.loading = true | |
| 182 | + const params = { | |
| 183 | + ...this.searchForm, | |
| 184 | + page: this.pagination.current, | |
| 185 | + row: this.pagination.size | |
| 186 | + } | |
| 187 | + | |
| 188 | + // 处理allNum | |
| 189 | + if (params.allNum && params.allNum.split('-').length === 3) { | |
| 190 | + const [floorNum, unitNum, roomNum] = params.allNum.split('-') | |
| 191 | + params.floorNum = floorNum.trim() | |
| 192 | + params.unitNum = unitNum.trim() | |
| 193 | + params.roomNum = roomNum.trim() | |
| 194 | + } else { | |
| 195 | + params.floorNum = '' | |
| 196 | + params.unitNum = '' | |
| 197 | + params.roomNum = '' | |
| 198 | + } | |
| 199 | + | |
| 200 | + const { data, total } = await listPropertyRightRegistration(params) | |
| 201 | + this.tableData = data | |
| 202 | + this.pagination.total = total | |
| 203 | + } catch (error) { | |
| 204 | + console.error('获取列表失败:', error) | |
| 205 | + this.$message.error(this.$t('propertyRightRegistration.fetchError')) | |
| 206 | + } finally { | |
| 207 | + this.loading = false | |
| 208 | + } | |
| 209 | + }, | |
| 210 | + async getDictData() { | |
| 211 | + try { | |
| 212 | + this.states = await getDict('property_right_registration', 'state') | |
| 213 | + } catch (error) { | |
| 214 | + console.error('获取字典数据失败:', error) | |
| 215 | + } | |
| 216 | + }, | |
| 217 | + async getFloors() { | |
| 218 | + try { | |
| 219 | + const params = { | |
| 220 | + communityId: this.searchForm.communityId, | |
| 221 | + page: 1, | |
| 222 | + row: 50 | |
| 223 | + } | |
| 224 | + const data = await queryFloors(params) | |
| 225 | + this.floors = data.apiFloorDataVoList || [] | |
| 226 | + } catch (error) { | |
| 227 | + console.error('获取楼栋数据失败:', error) | |
| 228 | + } | |
| 229 | + }, | |
| 230 | + async handleFloorChange(floorId) { | |
| 231 | + try { | |
| 232 | + const params = { | |
| 233 | + floorId, | |
| 234 | + communityId: this.searchForm.communityId, | |
| 235 | + page: 1, | |
| 236 | + row: 50 | |
| 237 | + } | |
| 238 | + const data = await queryUnits(params) | |
| 239 | + this.units = data || [] | |
| 240 | + this.searchForm.unitId = '' | |
| 241 | + } catch (error) { | |
| 242 | + console.error('获取单元数据失败:', error) | |
| 243 | + } | |
| 244 | + }, | |
| 245 | + handleSearch() { | |
| 246 | + this.pagination.current = 1 | |
| 247 | + this.getList() | |
| 248 | + }, | |
| 249 | + handleReset() { | |
| 250 | + this.searchForm = { | |
| 251 | + roomId: '', | |
| 252 | + allNum: '', | |
| 253 | + name: '', | |
| 254 | + link: '', | |
| 255 | + idCard: '', | |
| 256 | + address: '', | |
| 257 | + state: '', | |
| 258 | + floorId: '', | |
| 259 | + unitId: '', | |
| 260 | + communityId: getCommunityId() | |
| 261 | + } | |
| 262 | + this.units = [] | |
| 263 | + this.handleSearch() | |
| 264 | + }, | |
| 265 | + toggleMoreCondition() { | |
| 266 | + this.showMoreCondition = !this.showMoreCondition | |
| 267 | + }, | |
| 268 | + handleAdd() { | |
| 269 | + this.$refs.addForm.open() | |
| 270 | + }, | |
| 271 | + handleExamine(row) { | |
| 272 | + this.$refs.examineForm.open(row) | |
| 273 | + }, | |
| 274 | + handleEdit(row) { | |
| 275 | + this.$refs.editForm.open(row) | |
| 276 | + }, | |
| 277 | + handleDetail(row) { | |
| 278 | + this.$router.push({ | |
| 279 | + path: '/views/room/listPropertyRightRegistrationDetail', | |
| 280 | + query: { | |
| 281 | + prrId: row.prrId, | |
| 282 | + floorNum: row.floorNum, | |
| 283 | + unitNum: row.unitNum, | |
| 284 | + roomNum: row.roomNum | |
| 285 | + } | |
| 286 | + }) | |
| 287 | + }, | |
| 288 | + handleDelete(row) { | |
| 289 | + this.$refs.deleteForm.open(row) | |
| 290 | + }, | |
| 291 | + handleSuccess() { | |
| 292 | + this.getList() | |
| 293 | + }, | |
| 294 | + handleSizeChange(val) { | |
| 295 | + this.pagination.size = val | |
| 296 | + this.getList() | |
| 297 | + }, | |
| 298 | + handleCurrentChange(val) { | |
| 299 | + this.pagination.current = val | |
| 300 | + this.getList() | |
| 301 | + } | |
| 302 | + } | |
| 303 | +} | |
| 304 | +</script> | |
| 305 | + | |
| 306 | +<style lang="scss" scoped> | |
| 307 | +.property-right-registration-container { | |
| 308 | + padding: 0; | |
| 309 | + margin: 0; | |
| 310 | + | |
| 311 | + .search-wrapper { | |
| 312 | + margin-bottom: 20px; | |
| 313 | + | |
| 314 | + .el-row { | |
| 315 | + margin-bottom: 20px; | |
| 316 | + } | |
| 317 | + | |
| 318 | + .mt-20 { | |
| 319 | + margin-top: 20px; | |
| 320 | + } | |
| 321 | + } | |
| 322 | + | |
| 323 | + .list-wrapper { | |
| 324 | + .el-pagination { | |
| 325 | + margin-top: 20px; | |
| 326 | + text-align: right; | |
| 327 | + } | |
| 328 | + } | |
| 329 | +} | |
| 330 | +</style> | |
| 0 | 331 | \ No newline at end of file | ... | ... |
src/views/room/roomStructureLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + roomStructure: { | |
| 4 | + noOwner: 'None', | |
| 5 | + owe: 'Debt', | |
| 6 | + yuan: 'Yuan', | |
| 7 | + searchPlaceholder: 'Please enter room number like 1-1-1', | |
| 8 | + building: 'Building', | |
| 9 | + unit: 'Unit' | |
| 10 | + } | |
| 11 | + }, | |
| 12 | + zh: { | |
| 13 | + roomStructure: { | |
| 14 | + noOwner: '无', | |
| 15 | + owe: '欠费', | |
| 16 | + yuan: '元', | |
| 17 | + searchPlaceholder: '请输入房屋编号 楼栋-单元-房屋 如1-1-1', | |
| 18 | + building: '栋', | |
| 19 | + unit: '单元' | |
| 20 | + } | |
| 21 | + } | |
| 22 | +} | |
| 0 | 23 | \ No newline at end of file | ... | ... |
src/views/room/roomStructureList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="room-structure-container"> | |
| 3 | + <el-row :gutter="20"> | |
| 4 | + <el-col :span="4"> | |
| 5 | + <floor-unit-tree ref="floorUnitTree" @switchFloorUnit="switchFloorUnit" /> | |
| 6 | + </el-col> | |
| 7 | + <el-col :span="20"> | |
| 8 | + <el-card v-if="roomStructureInfo.layerRoomCount < 5" class="room-list-card"> | |
| 9 | + <el-row :gutter="10"> | |
| 10 | + <el-col v-for="(room, index) in roomStructureInfo.rooms" :key="index" :xs="12" :sm="8" :md="6" :lg="4" | |
| 11 | + :xl="3" class="room-item"> | |
| 12 | + <div class="room-card" :style="{ 'background-color': getBgColor(room) }" | |
| 13 | + @dblclick="toSimplifyAcceptance(room)"> | |
| 14 | + <div class="room-number">{{ room.floorNum }}-{{ room.unitNum }}-{{ room.roomNum }}</div> | |
| 15 | + <div class="room-state">{{ room.stateName }}</div> | |
| 16 | + <div class="room-owner">{{ room.ownerName || $t('roomStructure.noOwner') }}</div> | |
| 17 | + <div class="room-debt"> | |
| 18 | + <span>{{ $t('roomStructure.owe') }}</span>:{{ room.oweAmount }} | |
| 19 | + <span>{{ $t('roomStructure.yuan') }}</span> | |
| 20 | + </div> | |
| 21 | + </div> | |
| 22 | + </el-col> | |
| 23 | + </el-row> | |
| 24 | + </el-card> | |
| 25 | + | |
| 26 | + <el-card v-else class="room-list-card"> | |
| 27 | + <div v-for="(val, key, index) in roomStructureInfo.parkRooms" :key="index"> | |
| 28 | + <div class="floor-title">{{ key }}F</div> | |
| 29 | + <el-row :gutter="10" class="floor-row"> | |
| 30 | + <el-col v-for="(room, index) in val" :key="index" :xs="12" :sm="8" :md="6" :lg="4" :xl="3" | |
| 31 | + class="room-item"> | |
| 32 | + <div class="room-card" :style="{ 'background-color': getBgColor(room) }" | |
| 33 | + @dblclick="toSimplifyAcceptance(room)"> | |
| 34 | + <div class="room-number">{{ room.floorNum }}-{{ room.unitNum }}-{{ room.roomNum }}</div> | |
| 35 | + <div class="room-state">{{ room.stateName }}</div> | |
| 36 | + <div class="room-owner">{{ room.ownerName || $t('roomStructure.noOwner') }}</div> | |
| 37 | + <div class="room-debt"> | |
| 38 | + <span>{{ $t('roomStructure.owe') }}</span>:{{ room.oweAmount }} | |
| 39 | + <span>{{ $t('roomStructure.yuan') }}</span> | |
| 40 | + </div> | |
| 41 | + </div> | |
| 42 | + </el-col> | |
| 43 | + </el-row> | |
| 44 | + </div> | |
| 45 | + </el-card> | |
| 46 | + </el-col> | |
| 47 | + </el-row> | |
| 48 | + </div> | |
| 49 | +</template> | |
| 50 | + | |
| 51 | +<script> | |
| 52 | +import floorUnitTree from '@/components/room/floorUnitTree' | |
| 53 | +import { listRoomStructure } from '@/api/room/roomStructureApi' | |
| 54 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 55 | + | |
| 56 | +export default { | |
| 57 | + name: 'RoomStructureList', | |
| 58 | + components: { | |
| 59 | + floorUnitTree | |
| 60 | + }, | |
| 61 | + data() { | |
| 62 | + return { | |
| 63 | + roomStructureInfo: { | |
| 64 | + rooms: [], | |
| 65 | + parkRooms: {}, | |
| 66 | + layerRoomCount: 4 | |
| 67 | + }, | |
| 68 | + communityId: '' | |
| 69 | + } | |
| 70 | + }, | |
| 71 | + created() { | |
| 72 | + this.communityId = getCommunityId() | |
| 73 | + }, | |
| 74 | + methods: { | |
| 75 | + handleSwitchUnit(params) { | |
| 76 | + this.loadRooms(params.unitId) | |
| 77 | + }, | |
| 78 | + switchFloorUnit(data) { | |
| 79 | + if (!data.unitId) { | |
| 80 | + return | |
| 81 | + } | |
| 82 | + this.loadRooms(data.unitId) | |
| 83 | + }, | |
| 84 | + async loadRooms(unitId) { | |
| 85 | + this.roomStructureInfo.rooms = [] | |
| 86 | + this.roomStructureInfo.parkRooms = {} | |
| 87 | + | |
| 88 | + try { | |
| 89 | + const params = { | |
| 90 | + page: 1, | |
| 91 | + row: 100, | |
| 92 | + unitId: unitId, | |
| 93 | + communityId: this.communityId | |
| 94 | + } | |
| 95 | + | |
| 96 | + const { data } = await listRoomStructure(params) | |
| 97 | + this.roomStructureInfo.rooms = data | |
| 98 | + this.supportPark() | |
| 99 | + } catch (error) { | |
| 100 | + console.error('Failed to load rooms:', error) | |
| 101 | + } | |
| 102 | + }, | |
| 103 | + getBgColor(room) { | |
| 104 | + if (room.oweAmount > 0) { | |
| 105 | + return "#DC3545" | |
| 106 | + } | |
| 107 | + if (!room.ownerName) { | |
| 108 | + return "#1AB394" | |
| 109 | + } | |
| 110 | + if (room.state === '2001') { | |
| 111 | + return '#1296db' | |
| 112 | + } | |
| 113 | + if (room.state === '2003') { | |
| 114 | + return '#4C8CDE' | |
| 115 | + } | |
| 116 | + if (room.state === '2005') { | |
| 117 | + return '#085DC9' | |
| 118 | + } | |
| 119 | + if (room.state === '2004') { | |
| 120 | + return '#9DBFEA' | |
| 121 | + } | |
| 122 | + if (room.state === '2006') { | |
| 123 | + return '#365A87' | |
| 124 | + } | |
| 125 | + if (room.state === '2007') { | |
| 126 | + return '#1053A8' | |
| 127 | + } | |
| 128 | + if (room.state === '2008') { | |
| 129 | + return '#4E79AF' | |
| 130 | + } | |
| 131 | + if (room.state === '2009') { | |
| 132 | + return '#5B81B1' | |
| 133 | + } | |
| 134 | + return "#1296db" | |
| 135 | + }, | |
| 136 | + toSimplifyAcceptance(room) { | |
| 137 | + const date = new Date() | |
| 138 | + this.$store.dispatch('app/saveData', { | |
| 139 | + key: "JAVA110_IS_BACK", | |
| 140 | + value: date.getTime() | |
| 141 | + }) | |
| 142 | + this.$store.dispatch('app/saveData', { | |
| 143 | + key: 'simplifyAcceptanceSearch', | |
| 144 | + value: { | |
| 145 | + searchType: '1', | |
| 146 | + searchValue: `${room.floorNum}-${room.unitNum}-${room.roomNum}`, | |
| 147 | + searchPlaceholder: this.$t('roomStructure.searchPlaceholder') | |
| 148 | + } | |
| 149 | + }) | |
| 150 | + this.$router.push('/pages/property/simplifyAcceptance?tab=业务受理') | |
| 151 | + }, | |
| 152 | + supportPark() { | |
| 153 | + const parkRooms = this.roomStructureInfo.parkRooms | |
| 154 | + if (!this.roomStructureInfo.rooms || this.roomStructureInfo.rooms.length < 1) { | |
| 155 | + return | |
| 156 | + } | |
| 157 | + | |
| 158 | + this.roomStructureInfo.rooms.forEach(item => { | |
| 159 | + if (!parkRooms[item.layer]) { | |
| 160 | + parkRooms[item.layer] = [] | |
| 161 | + } | |
| 162 | + parkRooms[item.layer].push(item) | |
| 163 | + }) | |
| 164 | + | |
| 165 | + this.roomStructureInfo.parkRooms = parkRooms | |
| 166 | + if (Object.keys(parkRooms).length > 0) { | |
| 167 | + this.roomStructureInfo.layerRoomCount = parkRooms[Object.keys(parkRooms)[0]].length | |
| 168 | + } | |
| 169 | + } | |
| 170 | + } | |
| 171 | +} | |
| 172 | +</script> | |
| 173 | + | |
| 174 | +<style lang="scss" scoped> | |
| 175 | +.room-structure-container { | |
| 176 | + padding: 20px; | |
| 177 | + height: 100%; | |
| 178 | + | |
| 179 | + .room-list-card { | |
| 180 | + margin-bottom: 20px; | |
| 181 | + min-height: calc(100vh - 120px); | |
| 182 | + | |
| 183 | + .floor-title { | |
| 184 | + font-size: 16px; | |
| 185 | + font-weight: bold; | |
| 186 | + padding: 10px 0; | |
| 187 | + } | |
| 188 | + | |
| 189 | + .floor-row { | |
| 190 | + margin-bottom: 20px; | |
| 191 | + } | |
| 192 | + | |
| 193 | + .room-item { | |
| 194 | + margin-bottom: 10px; | |
| 195 | + } | |
| 196 | + | |
| 197 | + .room-card { | |
| 198 | + color: #fff; | |
| 199 | + border-radius: 5px; | |
| 200 | + padding: 10px; | |
| 201 | + cursor: pointer; | |
| 202 | + transition: all 0.3s; | |
| 203 | + height: 100%; | |
| 204 | + | |
| 205 | + &:hover { | |
| 206 | + transform: translateY(-3px); | |
| 207 | + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | |
| 208 | + } | |
| 209 | + | |
| 210 | + .room-number { | |
| 211 | + font-weight: bold; | |
| 212 | + margin-bottom: 5px; | |
| 213 | + } | |
| 214 | + | |
| 215 | + .room-state, | |
| 216 | + .room-owner, | |
| 217 | + .room-debt { | |
| 218 | + margin-bottom: 3px; | |
| 219 | + font-size: 12px; | |
| 220 | + } | |
| 221 | + } | |
| 222 | + } | |
| 223 | +} | |
| 224 | +</style> | |
| 0 | 225 | \ No newline at end of file | ... | ... |
src/views/system/assetImportLogDetailLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + assetImportLogDetail: { | |
| 4 | + title: 'Import Details', | |
| 5 | + importTime: 'Import Time', | |
| 6 | + status: 'Status', | |
| 7 | + description: 'Description', | |
| 8 | + all: 'All', | |
| 9 | + waitingImport: 'Waiting Import', | |
| 10 | + success: 'Success', | |
| 11 | + failed: 'Failed', | |
| 12 | + refresh: 'Refresh', | |
| 13 | + back: 'Back' | |
| 14 | + } | |
| 15 | + }, | |
| 16 | + zh: { | |
| 17 | + assetImportLogDetail: { | |
| 18 | + title: '导入详情', | |
| 19 | + importTime: '导入时间', | |
| 20 | + status: '状态', | |
| 21 | + description: '描述', | |
| 22 | + all: '全部', | |
| 23 | + waitingImport: '待导入', | |
| 24 | + success: '成功', | |
| 25 | + failed: '失败', | |
| 26 | + refresh: '刷新', | |
| 27 | + back: '返回' | |
| 28 | + } | |
| 29 | + } | |
| 30 | +} | |
| 0 | 31 | \ No newline at end of file | ... | ... |
src/views/system/assetImportLogDetailList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="animated fadeInRight ecommerce"> | |
| 3 | + <el-row> | |
| 4 | + <el-col :span="4" class="padding-r-0"> | |
| 5 | + <div class="border-radius"> | |
| 6 | + <div class="margin-xs-r treeview attendance-staff"> | |
| 7 | + <ul class="list-group text-center border-radius"> | |
| 8 | + <li class="list-group-item node-orgTree" v-for="(item, index) in assetImportLogDetailInfo.states" | |
| 9 | + :key="index" @click="swatchDetailState(item)" | |
| 10 | + :class="{ 'vc-node-selected': assetImportLogDetailInfo.state === item.value }"> | |
| 11 | + {{ item.name }} | |
| 12 | + </li> | |
| 13 | + </ul> | |
| 14 | + </div> | |
| 15 | + </div> | |
| 16 | + </el-col> | |
| 17 | + <el-col :span="20"> | |
| 18 | + <el-card> | |
| 19 | + <div slot="header" class="clearfix"> | |
| 20 | + <span>{{ $t('assetImportLogDetail.title') }}</span> | |
| 21 | + <div class="ibox-tools" style="float: right;"> | |
| 22 | + <el-button type="primary" size="small" @click="queryAssetImportLogDetail()"> | |
| 23 | + {{ $t('common.refresh') }} | |
| 24 | + </el-button> | |
| 25 | + <el-button size="small" @click="_goBack()"> | |
| 26 | + {{ $t('common.back') }} | |
| 27 | + </el-button> | |
| 28 | + </div> | |
| 29 | + </div> | |
| 30 | + <el-table :data="assetImportLogDetailInfo.logs" border style="width: 100%" v-loading="loading"> | |
| 31 | + <el-table-column v-for="(item, index) in assetImportLogDetailInfo.logTypes" :key="index" | |
| 32 | + :label="item.logColumn" align="center"> | |
| 33 | + <template slot-scope="scope"> | |
| 34 | + <div class="textAuto" style="max-width: 200px;"> | |
| 35 | + {{ scope.row[item.logProperty] }} | |
| 36 | + </div> | |
| 37 | + </template> | |
| 38 | + </el-table-column> | |
| 39 | + <el-table-column :label="$t('assetImportLogDetail.importTime')" align="center"> | |
| 40 | + <template slot-scope="scope"> | |
| 41 | + {{ scope.row.createTime }} | |
| 42 | + </template> | |
| 43 | + </el-table-column> | |
| 44 | + <el-table-column :label="$t('assetImportLogDetail.status')" align="center"> | |
| 45 | + <template slot-scope="scope"> | |
| 46 | + <span v-if="scope.row.state === 'W'">{{ $t('assetImportLogDetail.waitingImport') }}</span> | |
| 47 | + <span v-else-if="scope.row.state === 'C'">{{ $t('assetImportLogDetail.success') }}</span> | |
| 48 | + <span v-else>{{ $t('assetImportLogDetail.failed') }}</span> | |
| 49 | + </template> | |
| 50 | + </el-table-column> | |
| 51 | + <el-table-column :label="$t('assetImportLogDetail.description')" align="center"> | |
| 52 | + <template slot-scope="scope"> | |
| 53 | + <div class="textAuto" style="max-width: 200px;"> | |
| 54 | + {{ scope.row.message }} | |
| 55 | + </div> | |
| 56 | + </template> | |
| 57 | + </el-table-column> | |
| 58 | + </el-table> | |
| 59 | + <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" | |
| 60 | + :current-page="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size" | |
| 61 | + layout="total, sizes, prev, pager, next, jumper" :total="assetImportLogDetailInfo.total" | |
| 62 | + class="pagination"></el-pagination> | |
| 63 | + </el-card> | |
| 64 | + </el-col> | |
| 65 | + </el-row> | |
| 66 | + </div> | |
| 67 | +</template> | |
| 68 | + | |
| 69 | +<script> | |
| 70 | +import { queryAssetImportLogDetail, queryAssetImportLogType } from '@/api/system/assetImportLogDetailApi' | |
| 71 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 72 | + | |
| 73 | +export default { | |
| 74 | + name: 'AssetImportLogDetailList', | |
| 75 | + data() { | |
| 76 | + return { | |
| 77 | + loading: false, | |
| 78 | + assetImportLogDetailInfo: { | |
| 79 | + logs: [], | |
| 80 | + logTypes: [], | |
| 81 | + states: [ | |
| 82 | + { name: this.$t('assetImportLogDetail.all'), value: '' }, | |
| 83 | + { name: this.$t('assetImportLogDetail.waitingImport'), value: 'W' }, | |
| 84 | + { name: this.$t('assetImportLogDetail.success'), value: 'C' }, | |
| 85 | + { name: this.$t('assetImportLogDetail.failed'), value: 'F' } | |
| 86 | + ], | |
| 87 | + total: 0, | |
| 88 | + records: 1, | |
| 89 | + logId: '', | |
| 90 | + logType: '', | |
| 91 | + state: '' | |
| 92 | + }, | |
| 93 | + page: { | |
| 94 | + current: 1, | |
| 95 | + size: 10 | |
| 96 | + }, | |
| 97 | + communityId: '' | |
| 98 | + } | |
| 99 | + }, | |
| 100 | + created() { | |
| 101 | + this.communityId = getCommunityId() | |
| 102 | + }, | |
| 103 | + mounted() { | |
| 104 | + this.assetImportLogDetailInfo.logId = this.$route.query.logId | |
| 105 | + this.assetImportLogDetailInfo.logType = this.$route.query.logType | |
| 106 | + this.queryAssetImportLogType() | |
| 107 | + }, | |
| 108 | + methods: { | |
| 109 | + async _listAssetImportLogDetails(page, size) { | |
| 110 | + try { | |
| 111 | + this.loading = true | |
| 112 | + const params = { | |
| 113 | + page: page, | |
| 114 | + row: size, | |
| 115 | + logId: this.assetImportLogDetailInfo.logId, | |
| 116 | + communityId: this.communityId, | |
| 117 | + state: this.assetImportLogDetailInfo.state | |
| 118 | + } | |
| 119 | + const { data, total } = await queryAssetImportLogDetail(params) | |
| 120 | + this.assetImportLogDetailInfo.logs = data | |
| 121 | + this.assetImportLogDetailInfo.total = total | |
| 122 | + } catch (error) { | |
| 123 | + console.error('请求失败:', error) | |
| 124 | + } finally { | |
| 125 | + this.loading = false | |
| 126 | + } | |
| 127 | + }, | |
| 128 | + async queryAssetImportLogType() { | |
| 129 | + try { | |
| 130 | + const params = { | |
| 131 | + logType: this.assetImportLogDetailInfo.logType | |
| 132 | + } | |
| 133 | + const { data } = await queryAssetImportLogType(params) | |
| 134 | + this.assetImportLogDetailInfo.logTypes = data | |
| 135 | + this._listAssetImportLogDetails(this.page.current, this.page.size) | |
| 136 | + } catch (error) { | |
| 137 | + console.error('请求失败:', error) | |
| 138 | + } | |
| 139 | + }, | |
| 140 | + _goBack() { | |
| 141 | + this.$router.go(-1) | |
| 142 | + }, | |
| 143 | + queryAssetImportLogDetail() { | |
| 144 | + this._listAssetImportLogDetails(this.page.current, this.page.size) | |
| 145 | + }, | |
| 146 | + swatchDetailState(item) { | |
| 147 | + this.assetImportLogDetailInfo.state = item.value | |
| 148 | + this._listAssetImportLogDetails(this.page.current, this.page.size) | |
| 149 | + }, | |
| 150 | + handleSizeChange(val) { | |
| 151 | + this.page.size = val | |
| 152 | + this._listAssetImportLogDetails(this.page.current, val) | |
| 153 | + }, | |
| 154 | + handleCurrentChange(val) { | |
| 155 | + this.page.current = val | |
| 156 | + this._listAssetImportLogDetails(val, this.page.size) | |
| 157 | + } | |
| 158 | + } | |
| 159 | +} | |
| 160 | +</script> | |
| 161 | + | |
| 162 | +<style scoped> | |
| 163 | +.animated { | |
| 164 | + padding: 15px; | |
| 165 | +} | |
| 166 | + | |
| 167 | +.padding-r-0 { | |
| 168 | + padding-right: 0; | |
| 169 | +} | |
| 170 | + | |
| 171 | +.border-radius { | |
| 172 | + border-radius: 4px; | |
| 173 | +} | |
| 174 | + | |
| 175 | +.margin-xs-r { | |
| 176 | + margin-right: 5px; | |
| 177 | +} | |
| 178 | + | |
| 179 | +.list-group { | |
| 180 | + padding-left: 0; | |
| 181 | + margin-bottom: 0; | |
| 182 | +} | |
| 183 | + | |
| 184 | +.list-group-item { | |
| 185 | + position: relative; | |
| 186 | + display: block; | |
| 187 | + padding: 10px 15px; | |
| 188 | + margin-bottom: -1px; | |
| 189 | + background-color: #fff; | |
| 190 | + border: 1px solid #ddd; | |
| 191 | + cursor: pointer; | |
| 192 | +} | |
| 193 | + | |
| 194 | +.list-group-item:hover { | |
| 195 | + background-color: #f5f5f5; | |
| 196 | +} | |
| 197 | + | |
| 198 | +.vc-node-selected { | |
| 199 | + background-color: #409EFF; | |
| 200 | + color: #fff; | |
| 201 | +} | |
| 202 | + | |
| 203 | +.textAuto { | |
| 204 | + white-space: nowrap; | |
| 205 | + overflow: hidden; | |
| 206 | + text-overflow: ellipsis; | |
| 207 | +} | |
| 208 | + | |
| 209 | +.pagination { | |
| 210 | + margin-top: 15px; | |
| 211 | + text-align: right; | |
| 212 | +} | |
| 213 | +</style> | |
| 0 | 214 | \ No newline at end of file | ... | ... |
src/views/system/assetImportLogLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + assetImportLog: { | |
| 4 | + title: 'Batch Import Log', | |
| 5 | + logId: 'Import ID', | |
| 6 | + logTypeName: 'Import Type', | |
| 7 | + waitCount: 'Pending Count', | |
| 8 | + successCount: 'Success Count', | |
| 9 | + errorCount: 'Failed Count', | |
| 10 | + state: 'Status', | |
| 11 | + stateW: 'Pending', | |
| 12 | + stateC: 'Completed', | |
| 13 | + stateP: 'Processing', | |
| 14 | + createTime: 'Import Time', | |
| 15 | + remark: 'Remark', | |
| 16 | + fetchError: 'Failed to fetch import logs' | |
| 17 | + } | |
| 18 | + }, | |
| 19 | + zh: { | |
| 20 | + assetImportLog: { | |
| 21 | + title: '批量导入日志', | |
| 22 | + logId: '导入编号', | |
| 23 | + logTypeName: '导入类型', | |
| 24 | + waitCount: '待导入数', | |
| 25 | + successCount: '成功数量', | |
| 26 | + errorCount: '失败数量', | |
| 27 | + state: '状态', | |
| 28 | + stateW: '待导入', | |
| 29 | + stateC: '已完成', | |
| 30 | + stateP: '处理中', | |
| 31 | + createTime: '导入时间', | |
| 32 | + remark: '备注', | |
| 33 | + fetchError: '获取导入日志失败' | |
| 34 | + } | |
| 35 | + } | |
| 36 | +} | |
| 0 | 37 | \ No newline at end of file | ... | ... |
src/views/system/assetImportLogList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="asset-import-log-container animated fadeInRight"> | |
| 3 | + <el-card class="box-card"> | |
| 4 | + <div slot="header" class="flex justify-between"> | |
| 5 | + <span>{{ $t('assetImportLog.title') }}</span> | |
| 6 | + <div class="header-tools"> | |
| 7 | + <el-button type="primary" size="small" @click="_queryData"> | |
| 8 | + <i class="el-icon-refresh"></i>{{ $t('common.refresh') }} | |
| 9 | + </el-button> | |
| 10 | + </div> | |
| 11 | + </div> | |
| 12 | + | |
| 13 | + <el-row :gutter="20"> | |
| 14 | + <el-col :span="24"> | |
| 15 | + <el-table v-loading="loading" :data="assetImportLogInfo.logs" border style="width: 100%"> | |
| 16 | + <el-table-column prop="logId" :label="$t('assetImportLog.logId')" align="center" /> | |
| 17 | + <el-table-column prop="logTypeName" :label="$t('assetImportLog.logTypeName')" align="center" /> | |
| 18 | + <el-table-column prop="waitCount" :label="$t('assetImportLog.waitCount')" align="center" /> | |
| 19 | + <el-table-column prop="successCount" :label="$t('assetImportLog.successCount')" align="center" /> | |
| 20 | + <el-table-column prop="errorCount" :label="$t('assetImportLog.errorCount')" align="center" /> | |
| 21 | + <el-table-column prop="state" :label="$t('assetImportLog.state')" align="center"> | |
| 22 | + <template slot-scope="scope"> | |
| 23 | + <span v-if="scope.row.state === 'W'">{{ $t('assetImportLog.stateW') }}</span> | |
| 24 | + <span v-else-if="scope.row.state === 'C'">{{ $t('assetImportLog.stateC') }}</span> | |
| 25 | + <span v-else>{{ $t('assetImportLog.stateP') }}</span> | |
| 26 | + </template> | |
| 27 | + </el-table-column> | |
| 28 | + <el-table-column prop="createTime" :label="$t('assetImportLog.createTime')" align="center" /> | |
| 29 | + <el-table-column prop="remark" :label="$t('assetImportLog.remark')" align="center" /> | |
| 30 | + <el-table-column :label="$t('common.operation')" align="center" width="150"> | |
| 31 | + <template slot-scope="scope"> | |
| 32 | + <el-button size="mini" @click="_openDetail(scope.row)">{{ $t('common.detail') }}</el-button> | |
| 33 | + </template> | |
| 34 | + </el-table-column> | |
| 35 | + </el-table> | |
| 36 | + | |
| 37 | + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size" | |
| 38 | + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" | |
| 39 | + @current-change="handleCurrentChange" /> | |
| 40 | + </el-col> | |
| 41 | + </el-row> | |
| 42 | + </el-card> | |
| 43 | + </div> | |
| 44 | +</template> | |
| 45 | + | |
| 46 | +<script> | |
| 47 | +import { queryAssetImportLog } from '@/api/system/assetImportLogApi' | |
| 48 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 49 | + | |
| 50 | +export default { | |
| 51 | + name: 'AssetImportLogList', | |
| 52 | + data() { | |
| 53 | + return { | |
| 54 | + loading: false, | |
| 55 | + assetImportLogInfo: { | |
| 56 | + logs: [], | |
| 57 | + total: 0 | |
| 58 | + }, | |
| 59 | + page: { | |
| 60 | + current: 1, | |
| 61 | + size: 10, | |
| 62 | + total: 0 | |
| 63 | + }, | |
| 64 | + communityId: '' | |
| 65 | + } | |
| 66 | + }, | |
| 67 | + created() { | |
| 68 | + this.communityId = getCommunityId() | |
| 69 | + this._listAssetImportLogs(this.page.current, this.page.size) | |
| 70 | + }, | |
| 71 | + methods: { | |
| 72 | + async _listAssetImportLogs(page, size) { | |
| 73 | + try { | |
| 74 | + this.loading = true | |
| 75 | + const params = { | |
| 76 | + page, | |
| 77 | + row: size, | |
| 78 | + communityId: this.communityId | |
| 79 | + } | |
| 80 | + const { data, total } = await queryAssetImportLog(params) | |
| 81 | + this.assetImportLogInfo.logs = data | |
| 82 | + this.page.total = total | |
| 83 | + } catch (error) { | |
| 84 | + this.$message.error(this.$t('assetImportLog.fetchError')) | |
| 85 | + } finally { | |
| 86 | + this.loading = false | |
| 87 | + } | |
| 88 | + }, | |
| 89 | + _queryData() { | |
| 90 | + this.page.current = 1 | |
| 91 | + this._listAssetImportLogs(this.page.current, this.page.size) | |
| 92 | + }, | |
| 93 | + _openDetail(log) { | |
| 94 | + this.$router.push({ | |
| 95 | + path: '/views/system/assetImportLogDetail', | |
| 96 | + query: { | |
| 97 | + logId: log.logId, | |
| 98 | + logType: log.logType | |
| 99 | + } | |
| 100 | + }) | |
| 101 | + }, | |
| 102 | + handleSizeChange(val) { | |
| 103 | + this.page.size = val | |
| 104 | + this._listAssetImportLogs(this.page.current, this.page.size) | |
| 105 | + }, | |
| 106 | + handleCurrentChange(val) { | |
| 107 | + this.page.current = val | |
| 108 | + this._listAssetImportLogs(this.page.current, this.page.size) | |
| 109 | + } | |
| 110 | + } | |
| 111 | +} | |
| 112 | +</script> | |
| 113 | + | |
| 114 | +<style lang="scss" scoped> | |
| 115 | +.asset-import-log-container { | |
| 116 | + padding: 20px; | |
| 117 | + | |
| 118 | + .box-card { | |
| 119 | + margin-bottom: 20px; | |
| 120 | + } | |
| 121 | + | |
| 122 | + .header-tools { | |
| 123 | + float: right; | |
| 124 | + } | |
| 125 | + | |
| 126 | + .el-pagination { | |
| 127 | + margin-top: 20px; | |
| 128 | + text-align: right; | |
| 129 | + } | |
| 130 | +} | |
| 131 | +</style> | |
| 0 | 132 | \ No newline at end of file | ... | ... |
src/views/system/downloadTempFileLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + downloadTempFile: { | |
| 4 | + title: 'Download Center', | |
| 5 | + refresh: 'Refresh', | |
| 6 | + name: 'Name', | |
| 7 | + fileType: 'File Type', | |
| 8 | + downloadUser: 'Downloader', | |
| 9 | + downloadTime: 'Download Time', | |
| 10 | + status: 'Status', | |
| 11 | + remark: 'Remark', | |
| 12 | + operation: 'Operation', | |
| 13 | + download: 'Download', | |
| 14 | + delete: 'Delete', | |
| 15 | + fetchError: 'Failed to fetch file list' | |
| 16 | + }, | |
| 17 | + deleteDownloadTempFile: { | |
| 18 | + title: 'Please confirm your operation', | |
| 19 | + confirmMessage: 'Are you sure to delete this file?', | |
| 20 | + cancel: 'Cancel', | |
| 21 | + confirm: 'Confirm Delete', | |
| 22 | + deleteSuccess: 'File deleted successfully', | |
| 23 | + deleteFailed: 'Failed to delete file' | |
| 24 | + } | |
| 25 | + }, | |
| 26 | + zh: { | |
| 27 | + downloadTempFile: { | |
| 28 | + title: '下载中心', | |
| 29 | + refresh: '刷新', | |
| 30 | + name: '名称', | |
| 31 | + fileType: '文件类型', | |
| 32 | + downloadUser: '下载人', | |
| 33 | + downloadTime: '下载时间', | |
| 34 | + status: '状态', | |
| 35 | + remark: '备注', | |
| 36 | + operation: '操作', | |
| 37 | + download: '下载', | |
| 38 | + delete: '删除', | |
| 39 | + fetchError: '获取文件列表失败' | |
| 40 | + }, | |
| 41 | + deleteDownloadTempFile: { | |
| 42 | + title: '请确认您的操作', | |
| 43 | + confirmMessage: '确定删除该文件吗?', | |
| 44 | + cancel: '取消', | |
| 45 | + confirm: '确认删除', | |
| 46 | + deleteSuccess: '文件删除成功', | |
| 47 | + deleteFailed: '文件删除失败' | |
| 48 | + } | |
| 49 | + } | |
| 50 | +} | |
| 0 | 51 | \ No newline at end of file | ... | ... |
src/views/system/downloadTempFileList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="download-temp-file-container"> | |
| 3 | + <el-card class="box-card"> | |
| 4 | + <div slot="header" class="flex justify-between"> | |
| 5 | + <span>{{ $t('downloadTempFile.title') }}</span> | |
| 6 | + <el-button type="primary" size="mini" style="float: right;" @click="_queryDownloadTempFileMethod"> | |
| 7 | + {{ $t('downloadTempFile.refresh') }} | |
| 8 | + </el-button> | |
| 9 | + </div> | |
| 10 | + | |
| 11 | + <el-table v-loading="loading" :data="downloadTempFileInfo.files" border style="width: 100%"> | |
| 12 | + <el-table-column prop="name" :label="$t('downloadTempFile.name')" align="center" /> | |
| 13 | + <el-table-column prop="fileTypeName" :label="$t('downloadTempFile.fileType')" align="center" /> | |
| 14 | + <el-table-column prop="downloadUserName" :label="$t('downloadTempFile.downloadUser')" align="center" /> | |
| 15 | + <el-table-column prop="createTime" :label="$t('downloadTempFile.downloadTime')" align="center" /> | |
| 16 | + <el-table-column prop="stateName" :label="$t('downloadTempFile.status')" align="center" /> | |
| 17 | + <el-table-column prop="remark" :label="$t('downloadTempFile.remark')" align="center" /> | |
| 18 | + <el-table-column :label="$t('downloadTempFile.operation')" align="center" width="200"> | |
| 19 | + <template slot-scope="scope"> | |
| 20 | + <el-button v-if="scope.row.state === '3003'" size="mini" type="primary" @click="_downLoadFile(scope.row)"> | |
| 21 | + {{ $t('downloadTempFile.download') }} | |
| 22 | + </el-button> | |
| 23 | + <el-button v-if="scope.row.state === '3003' || scope.row.state === '4004'" size="mini" type="danger" | |
| 24 | + @click="_openDeleteFileModel(scope.row)"> | |
| 25 | + {{ $t('downloadTempFile.delete') }} | |
| 26 | + </el-button> | |
| 27 | + </template> | |
| 28 | + </el-table-column> | |
| 29 | + </el-table> | |
| 30 | + | |
| 31 | + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size" | |
| 32 | + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" | |
| 33 | + @current-change="handleCurrentChange" /> | |
| 34 | + </el-card> | |
| 35 | + | |
| 36 | + <delete-download-temp-file ref="deleteDialog" @success="handleSuccess" /> | |
| 37 | + </div> | |
| 38 | +</template> | |
| 39 | + | |
| 40 | +<script> | |
| 41 | +import { listUserDownloadFile } from '@/api/system/downloadTempFileApi' | |
| 42 | +import DeleteDownloadTempFile from '@/components/system/deleteDownloadTempFile' | |
| 43 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 44 | + | |
| 45 | +export default { | |
| 46 | + name: 'DownloadTempFileList', | |
| 47 | + components: { | |
| 48 | + DeleteDownloadTempFile | |
| 49 | + }, | |
| 50 | + data() { | |
| 51 | + return { | |
| 52 | + loading: false, | |
| 53 | + downloadTempFileInfo: { | |
| 54 | + files: [], | |
| 55 | + conditions: { | |
| 56 | + communityId: '', | |
| 57 | + page: 1, | |
| 58 | + row: 10 | |
| 59 | + } | |
| 60 | + }, | |
| 61 | + page: { | |
| 62 | + current: 1, | |
| 63 | + size: 10, | |
| 64 | + total: 0 | |
| 65 | + } | |
| 66 | + } | |
| 67 | + }, | |
| 68 | + created() { | |
| 69 | + this.downloadTempFileInfo.conditions.communityId = getCommunityId() | |
| 70 | + this._listFiles(this.page.current, this.page.size) | |
| 71 | + }, | |
| 72 | + methods: { | |
| 73 | + async _listFiles(page, size) { | |
| 74 | + try { | |
| 75 | + this.loading = true | |
| 76 | + this.downloadTempFileInfo.conditions.page = page | |
| 77 | + this.downloadTempFileInfo.conditions.row = size | |
| 78 | + | |
| 79 | + const { data, total } = await listUserDownloadFile(this.downloadTempFileInfo.conditions) | |
| 80 | + this.downloadTempFileInfo.files = data | |
| 81 | + this.page.total = total | |
| 82 | + } catch (error) { | |
| 83 | + this.$message.error(this.$t('downloadTempFile.fetchError')) | |
| 84 | + } finally { | |
| 85 | + this.loading = false | |
| 86 | + } | |
| 87 | + }, | |
| 88 | + _downLoadFile(file) { | |
| 89 | + if (!file.tempUrl) { | |
| 90 | + this.$message.error(this.$t('downloadTempFile.downloadFailed')) | |
| 91 | + return | |
| 92 | + } | |
| 93 | + window.open(file.downloadUrl, '_blank') | |
| 94 | + }, | |
| 95 | + _openDeleteFileModel(file) { | |
| 96 | + this.$refs.deleteDialog.open(file) | |
| 97 | + }, | |
| 98 | + _queryDownloadTempFileMethod() { | |
| 99 | + this.page.current = 1 | |
| 100 | + this._listFiles(this.page.current, this.page.size) | |
| 101 | + }, | |
| 102 | + handleSuccess() { | |
| 103 | + this._listFiles(this.page.current, this.page.size) | |
| 104 | + }, | |
| 105 | + handleSizeChange(val) { | |
| 106 | + this.page.size = val | |
| 107 | + this._listFiles(this.page.current, val) | |
| 108 | + }, | |
| 109 | + handleCurrentChange(val) { | |
| 110 | + this.page.current = val | |
| 111 | + this._listFiles(val, this.page.size) | |
| 112 | + } | |
| 113 | + } | |
| 114 | +} | |
| 115 | +</script> | |
| 116 | + | |
| 117 | +<style lang="scss" scoped> | |
| 118 | +.download-temp-file-container { | |
| 119 | + padding: 20px; | |
| 120 | + | |
| 121 | + .box-card { | |
| 122 | + margin-bottom: 20px; | |
| 123 | + } | |
| 124 | + | |
| 125 | + .el-pagination { | |
| 126 | + margin-top: 20px; | |
| 127 | + text-align: right; | |
| 128 | + } | |
| 129 | +} | |
| 130 | +</style> | |
| 0 | 131 | \ No newline at end of file | ... | ... |
src/views/system/feePrintPageManageLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + feePrintPageManage: { | |
| 4 | + search: { | |
| 5 | + title: 'Search Conditions', | |
| 6 | + pageId: 'Receipt ID', | |
| 7 | + pageName: 'Name', | |
| 8 | + state: 'Status', | |
| 9 | + all: 'All', | |
| 10 | + enabled: 'Enabled', | |
| 11 | + disabled: 'Disabled' | |
| 12 | + }, | |
| 13 | + list: { | |
| 14 | + title: 'Receipt Template' | |
| 15 | + }, | |
| 16 | + table: { | |
| 17 | + pageId: 'Receipt ID', | |
| 18 | + pageName: 'Name', | |
| 19 | + communityId: 'Community ID', | |
| 20 | + templateName: 'Receipt Page', | |
| 21 | + state: 'Status', | |
| 22 | + enabled: 'Enabled', | |
| 23 | + disabled: 'Disabled', | |
| 24 | + operation: 'Operation' | |
| 25 | + }, | |
| 26 | + add: { | |
| 27 | + title: 'Add Receipt Template', | |
| 28 | + pageName: 'Name', | |
| 29 | + pageNamePlaceholder: 'Please enter name', | |
| 30 | + template: 'Receipt Page', | |
| 31 | + templatePlaceholder: 'Please select receipt page', | |
| 32 | + success: 'Add successfully' | |
| 33 | + }, | |
| 34 | + edit: { | |
| 35 | + title: 'Edit Receipt Template', | |
| 36 | + pageName: 'Name', | |
| 37 | + pageNamePlaceholder: 'Please enter name', | |
| 38 | + template: 'Receipt Page', | |
| 39 | + templatePlaceholder: 'Please select receipt page', | |
| 40 | + success: 'Update successfully' | |
| 41 | + }, | |
| 42 | + delete: { | |
| 43 | + title: 'Delete Confirmation', | |
| 44 | + confirmText: 'Are you sure to delete this receipt template?', | |
| 45 | + success: 'Delete successfully' | |
| 46 | + }, | |
| 47 | + validate: { | |
| 48 | + pageNameRequired: 'Name is required', | |
| 49 | + pageNameMaxLength: 'Name cannot exceed 128 characters', | |
| 50 | + templateRequired: 'Receipt page is required', | |
| 51 | + pageIdRequired: 'Receipt ID is required' | |
| 52 | + }, | |
| 53 | + fetchError: 'Failed to fetch data' | |
| 54 | + } | |
| 55 | + }, | |
| 56 | + zh: { | |
| 57 | + feePrintPageManage: { | |
| 58 | + search: { | |
| 59 | + title: '查询条件', | |
| 60 | + pageId: '收据ID', | |
| 61 | + pageName: '名称', | |
| 62 | + state: '状态', | |
| 63 | + all: '全部', | |
| 64 | + enabled: '启用', | |
| 65 | + disabled: '停用' | |
| 66 | + }, | |
| 67 | + list: { | |
| 68 | + title: '收据模板' | |
| 69 | + }, | |
| 70 | + table: { | |
| 71 | + pageId: '收据ID', | |
| 72 | + pageName: '名称', | |
| 73 | + communityId: '小区ID', | |
| 74 | + templateName: '收据页面', | |
| 75 | + state: '状态', | |
| 76 | + enabled: '启用', | |
| 77 | + disabled: '停用', | |
| 78 | + operation: '操作' | |
| 79 | + }, | |
| 80 | + add: { | |
| 81 | + title: '添加收据模板', | |
| 82 | + pageName: '名称', | |
| 83 | + pageNamePlaceholder: '请输入名称', | |
| 84 | + template: '收据页面', | |
| 85 | + templatePlaceholder: '请选择收据页面', | |
| 86 | + success: '添加成功' | |
| 87 | + }, | |
| 88 | + edit: { | |
| 89 | + title: '修改收据模板', | |
| 90 | + pageName: '名称', | |
| 91 | + pageNamePlaceholder: '请输入名称', | |
| 92 | + template: '收据页面', | |
| 93 | + templatePlaceholder: '请选择收据页面', | |
| 94 | + success: '修改成功' | |
| 95 | + }, | |
| 96 | + delete: { | |
| 97 | + title: '删除确认', | |
| 98 | + confirmText: '确定删除该收据模板吗?', | |
| 99 | + success: '删除成功' | |
| 100 | + }, | |
| 101 | + validate: { | |
| 102 | + pageNameRequired: '名称不能为空', | |
| 103 | + pageNameMaxLength: '名称不能超过128个字符', | |
| 104 | + templateRequired: '收据页面不能为空', | |
| 105 | + pageIdRequired: '收据ID不能为空' | |
| 106 | + }, | |
| 107 | + fetchError: '获取数据失败' | |
| 108 | + } | |
| 109 | + } | |
| 110 | +} | |
| 0 | 111 | \ No newline at end of file | ... | ... |
src/views/system/feePrintPageManageList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="fee-print-page-manage-container"> | |
| 3 | + <!-- 查询条件 --> | |
| 4 | + <el-card class="search-wrapper"> | |
| 5 | + <div slot="header" class="flex justify-between"> | |
| 6 | + <span>{{ $t('feePrintPageManage.search.title') }}</span> | |
| 7 | + </div> | |
| 8 | + <el-row :gutter="20"> | |
| 9 | + <el-col :span="6"> | |
| 10 | + <el-input v-model="searchForm.pageId" :placeholder="$t('feePrintPageManage.search.pageId')" clearable /> | |
| 11 | + </el-col> | |
| 12 | + <el-col :span="6"> | |
| 13 | + <el-input v-model="searchForm.pageName" :placeholder="$t('feePrintPageManage.search.pageName')" clearable /> | |
| 14 | + </el-col> | |
| 15 | + <el-col :span="6"> | |
| 16 | + <el-select v-model="searchForm.state" :placeholder="$t('feePrintPageManage.search.state')" style="width:100%"> | |
| 17 | + <el-option :label="$t('feePrintPageManage.search.all')" value="" /> | |
| 18 | + <el-option :label="$t('feePrintPageManage.search.enabled')" value="T" /> | |
| 19 | + <el-option :label="$t('feePrintPageManage.search.disabled')" value="F" /> | |
| 20 | + </el-select> | |
| 21 | + </el-col> | |
| 22 | + <el-col :span="6"> | |
| 23 | + <el-button type="primary" @click="handleSearch"> | |
| 24 | + {{ $t('common.search') }} | |
| 25 | + </el-button> | |
| 26 | + <el-button @click="handleReset"> | |
| 27 | + {{ $t('common.reset') }} | |
| 28 | + </el-button> | |
| 29 | + </el-col> | |
| 30 | + </el-row> | |
| 31 | + </el-card> | |
| 32 | + | |
| 33 | + <!-- 收据模板列表 --> | |
| 34 | + <el-card class="list-wrapper"> | |
| 35 | + <div slot="header" class="flex justify-between"> | |
| 36 | + <span>{{ $t('feePrintPageManage.list.title') }}</span> | |
| 37 | + <el-button type="primary" size="small" style="float:right" @click="handleAdd"> | |
| 38 | + {{ $t('common.add') }} | |
| 39 | + </el-button> | |
| 40 | + </div> | |
| 41 | + | |
| 42 | + <el-table v-loading="loading" :data="tableData" border style="width:100%"> | |
| 43 | + <el-table-column prop="pageId" :label="$t('feePrintPageManage.table.pageId')" align="center" /> | |
| 44 | + <el-table-column prop="pageName" :label="$t('feePrintPageManage.table.pageName')" align="center" /> | |
| 45 | + <el-table-column prop="communityId" :label="$t('feePrintPageManage.table.communityId')" align="center" /> | |
| 46 | + <el-table-column prop="templateName" :label="$t('feePrintPageManage.table.templateName')" align="center" /> | |
| 47 | + <el-table-column :label="$t('feePrintPageManage.table.state')" align="center"> | |
| 48 | + <template slot-scope="scope"> | |
| 49 | + {{ scope.row.state === 'T' ? $t('feePrintPageManage.table.enabled') : | |
| 50 | + $t('feePrintPageManage.table.disabled') }} | |
| 51 | + </template> | |
| 52 | + </el-table-column> | |
| 53 | + <el-table-column :label="$t('common.operation')" align="center" width="200"> | |
| 54 | + <template slot-scope="scope"> | |
| 55 | + <el-button size="mini" type="primary" @click="handleEdit(scope.row)"> | |
| 56 | + {{ $t('common.edit') }} | |
| 57 | + </el-button> | |
| 58 | + <el-button v-if="scope.row.state === 'F'" size="mini" type="success" @click="handleEnable(scope.row)"> | |
| 59 | + {{ $t('common.enabled') }} | |
| 60 | + </el-button> | |
| 61 | + <el-button size="mini" type="danger" @click="handleDelete(scope.row)"> | |
| 62 | + {{ $t('common.delete') }} | |
| 63 | + </el-button> | |
| 64 | + </template> | |
| 65 | + </el-table-column> | |
| 66 | + </el-table> | |
| 67 | + | |
| 68 | + <el-pagination :current-page="pagination.current" :page-sizes="[10, 20, 30, 50]" :page-size="pagination.size" | |
| 69 | + :total="pagination.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" | |
| 70 | + @current-change="handleCurrentChange" /> | |
| 71 | + </el-card> | |
| 72 | + | |
| 73 | + <!-- 添加弹窗 --> | |
| 74 | + <add-fee-print-page ref="addDialog" @success="handleSuccess" /> | |
| 75 | + | |
| 76 | + <!-- 编辑弹窗 --> | |
| 77 | + <edit-fee-print-page ref="editDialog" @success="handleSuccess" /> | |
| 78 | + | |
| 79 | + <!-- 删除弹窗 --> | |
| 80 | + <delete-fee-print-page ref="deleteDialog" @success="handleSuccess" /> | |
| 81 | + </div> | |
| 82 | +</template> | |
| 83 | + | |
| 84 | +<script> | |
| 85 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 86 | +import { listFeePrintPage, updateFeePrintPageState } from '@/api/system/feePrintPageManageApi' | |
| 87 | +import AddFeePrintPage from '@/components/system/addFeePrintPage' | |
| 88 | +import EditFeePrintPage from '@/components/system/editFeePrintPage' | |
| 89 | +import DeleteFeePrintPage from '@/components/system/deleteFeePrintPage' | |
| 90 | + | |
| 91 | +export default { | |
| 92 | + name: 'FeePrintPageManageList', | |
| 93 | + components: { | |
| 94 | + AddFeePrintPage, | |
| 95 | + EditFeePrintPage, | |
| 96 | + DeleteFeePrintPage | |
| 97 | + }, | |
| 98 | + data() { | |
| 99 | + return { | |
| 100 | + loading: false, | |
| 101 | + communityId: '', | |
| 102 | + searchForm: { | |
| 103 | + pageId: '', | |
| 104 | + pageName: '', | |
| 105 | + state: '' | |
| 106 | + }, | |
| 107 | + tableData: [], | |
| 108 | + pagination: { | |
| 109 | + current: 1, | |
| 110 | + size: 10, | |
| 111 | + total: 0 | |
| 112 | + } | |
| 113 | + } | |
| 114 | + }, | |
| 115 | + created() { | |
| 116 | + this.communityId = getCommunityId() | |
| 117 | + this.getList() | |
| 118 | + }, | |
| 119 | + methods: { | |
| 120 | + async getList() { | |
| 121 | + try { | |
| 122 | + this.loading = true | |
| 123 | + const params = { | |
| 124 | + page: this.pagination.current, | |
| 125 | + row: this.pagination.size, | |
| 126 | + communityId: this.communityId, | |
| 127 | + ...this.searchForm | |
| 128 | + } | |
| 129 | + const { data, total } = await listFeePrintPage(params) | |
| 130 | + this.tableData = data | |
| 131 | + this.pagination.total = total | |
| 132 | + } catch (error) { | |
| 133 | + this.$message.error(this.$t('feePrintPageManage.fetchError')) | |
| 134 | + } finally { | |
| 135 | + this.loading = false | |
| 136 | + } | |
| 137 | + }, | |
| 138 | + handleSearch() { | |
| 139 | + this.pagination.current = 1 | |
| 140 | + this.getList() | |
| 141 | + }, | |
| 142 | + handleReset() { | |
| 143 | + this.searchForm = { | |
| 144 | + pageId: '', | |
| 145 | + pageName: '', | |
| 146 | + state: '' | |
| 147 | + } | |
| 148 | + this.handleSearch() | |
| 149 | + }, | |
| 150 | + handleAdd() { | |
| 151 | + this.$refs.addDialog.open() | |
| 152 | + }, | |
| 153 | + handleEdit(row) { | |
| 154 | + this.$refs.editDialog.open(row) | |
| 155 | + }, | |
| 156 | + handleEnable(row) { | |
| 157 | + // TODO: 启用逻辑 | |
| 158 | + console.log(row) | |
| 159 | + updateFeePrintPageState({ | |
| 160 | + pageId: row.pageId, | |
| 161 | + state: 'T' | |
| 162 | + }).then(() => { | |
| 163 | + this.$message.success(this.$t('feePrintPageManage.enableSuccess')) | |
| 164 | + this.getList() | |
| 165 | + }) | |
| 166 | + }, | |
| 167 | + handleDelete(row) { | |
| 168 | + this.$refs.deleteDialog.open(row) | |
| 169 | + }, | |
| 170 | + handleSuccess() { | |
| 171 | + this.getList() | |
| 172 | + }, | |
| 173 | + handleSizeChange(val) { | |
| 174 | + this.pagination.size = val | |
| 175 | + this.getList() | |
| 176 | + }, | |
| 177 | + handleCurrentChange(val) { | |
| 178 | + this.pagination.current = val | |
| 179 | + this.getList() | |
| 180 | + } | |
| 181 | + } | |
| 182 | +} | |
| 183 | +</script> | |
| 184 | + | |
| 185 | +<style lang="scss" scoped> | |
| 186 | +.fee-print-page-manage-container { | |
| 187 | + padding: 20px; | |
| 188 | + | |
| 189 | + .search-wrapper { | |
| 190 | + margin-bottom: 20px; | |
| 191 | + | |
| 192 | + .el-row { | |
| 193 | + margin-bottom: 20px; | |
| 194 | + } | |
| 195 | + } | |
| 196 | + | |
| 197 | + .list-wrapper { | |
| 198 | + margin-bottom: 20px; | |
| 199 | + } | |
| 200 | + | |
| 201 | + .el-pagination { | |
| 202 | + margin-top: 20px; | |
| 203 | + text-align: right; | |
| 204 | + } | |
| 205 | +} | |
| 206 | +</style> | |
| 0 | 207 | \ No newline at end of file | ... | ... |