add_info.html 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
  6. <title>秒过加资料信息维护</title>
  7. <script src="https://kylx365-1253256735.file.myqcloud.com/js/jquery-1.10.2.min.js"></script>
  8. <script src="https://kylx365-1253256735.file.myqcloud.com/js/vue.min.js"></script>
  9. <style>
  10. .main00 {
  11. width: 100%;
  12. height: 100vh;
  13. min-height: 600px;
  14. background: white;
  15. display: flex;
  16. flex-direction: column;
  17. overflow: hidden;
  18. }
  19. .ListTop {
  20. width: 100%;
  21. height: 60px;
  22. background: white;
  23. border-bottom: 1px solid #EEEEEE;
  24. justify-content: flex-start;
  25. flex-shrink: 0;
  26. }
  27. .ListTop3 {
  28. margin-left: 40px;
  29. height: 50px;
  30. align-items: center;
  31. }
  32. .title {
  33. font-size: 24px;
  34. color: #333333;
  35. font-weight: bold;
  36. }
  37. .main0 {
  38. width: 100%;
  39. background: white;
  40. flex: 1;
  41. min-height: 0;
  42. overflow: hidden;
  43. display: flex;
  44. position: relative;
  45. align-items: flex-start;
  46. }
  47. .search-panel {
  48. width: 400px;
  49. border-right: 1px solid #EEEEEE;
  50. background: #F9F9F9;
  51. display: flex;
  52. flex-direction: column;
  53. height: 100%;
  54. position: relative;
  55. flex-shrink: 0;
  56. }
  57. .search-box {
  58. padding: 15px;
  59. border-bottom: 1px solid #EEEEEE;
  60. background: white;
  61. width: 100%;
  62. justify-content: center;
  63. }
  64. .search-input {
  65. width: 200px;
  66. height: 32px;
  67. padding: 0 32px 0 12px;
  68. border: 1px solid #DDDDDD;
  69. border-radius: 4px;
  70. font-size: 14px;
  71. color: #333333;
  72. }
  73. .search-input:focus {
  74. border-color: #4A90E2;
  75. outline: none;
  76. }
  77. .btn33 {
  78. width: 32px;
  79. height: 32px;
  80. margin-left: 8px;
  81. border-radius: 4px;
  82. background: #F5F5F5;
  83. cursor: pointer;
  84. justify-content: center;
  85. align-items: center;
  86. }
  87. .btn33:hover {
  88. background: #EEEEEE;
  89. }
  90. .json-editor {
  91. width: calc(100% - 400px);
  92. padding: 20px;
  93. overflow-y: auto;
  94. height: calc(100% - 60px);
  95. }
  96. .json-node {
  97. margin: 5px 0;
  98. padding: 5px;
  99. font-family: monospace;
  100. }
  101. .json-key {
  102. color: #881391;
  103. margin-right: 5px;
  104. }
  105. .json-string {
  106. color: #268BD2;
  107. }
  108. .json-number {
  109. color: #859900;
  110. }
  111. .json-boolean {
  112. color: #CB4B16;
  113. }
  114. .json-null {
  115. color: #93A1A1;
  116. }
  117. .editable {
  118. cursor: pointer;
  119. }
  120. .editable:hover {
  121. background-color: #f0f0f0;
  122. }
  123. .json-editor-input {
  124. width: 100%;
  125. padding: 4px;
  126. font-family: monospace;
  127. border: 1px solid #4A90E2;
  128. border-radius: 4px;
  129. }
  130. .btn-group {
  131. position: fixed;
  132. bottom: 20px;
  133. right: 20px;
  134. text-align: right;
  135. padding: 20px;
  136. background: white;
  137. box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
  138. border-radius: 4px;
  139. }
  140. .btn {
  141. min-width: 30px;
  142. height: 36px;
  143. padding: 0 16px;
  144. margin-left: 12px;
  145. border: none;
  146. border-radius: 4px;
  147. cursor: pointer;
  148. font-size: 14px;
  149. transition: all 0.3s;
  150. }
  151. .btn-primary {
  152. background: #4A90E2;
  153. color: white;
  154. }
  155. .btn-primary:hover {
  156. background: #357ABD;
  157. }
  158. .btn-default {
  159. background: white;
  160. border: 1px solid #DDDDDD;
  161. color: #666666;
  162. }
  163. .btn-default:hover {
  164. background: #F5F5F5;
  165. border-color: #CCCCCC;
  166. }
  167. .toast {
  168. position: fixed;
  169. top: 20px;
  170. left: 50%;
  171. transform: translateX(-50%);
  172. padding: 12px 24px;
  173. background: rgba(0, 0, 0, 0.7);
  174. color: white;
  175. border-radius: 4px;
  176. z-index: 9999;
  177. opacity: 0;
  178. transition: opacity 0.3s;
  179. }
  180. .toast.show {
  181. opacity: 1;
  182. }
  183. .toast.success {
  184. background: #4CAF50;
  185. }
  186. .toast.error {
  187. background: #F44336;
  188. }
  189. .toast.info {
  190. background: #2196F3;
  191. }
  192. /* Loading样式 */
  193. .loading-overlay {
  194. position: absolute;
  195. top: 0;
  196. left: 0;
  197. width: 100%;
  198. height: 100%;
  199. background-color: rgba(255, 255, 255, 0.7);
  200. display: flex;
  201. justify-content: center;
  202. align-items: center;
  203. z-index: 100;
  204. }
  205. .loading-spinner {
  206. width: 50px;
  207. height: 50px;
  208. border: 5px solid #f3f3f3;
  209. border-top: 5px solid #4A90E2;
  210. border-radius: 50%;
  211. animation: spin 1s linear infinite;
  212. }
  213. @keyframes spin {
  214. 0% { transform: rotate(0deg); }
  215. 100% { transform: rotate(360deg); }
  216. }
  217. .clear-btn {
  218. position: absolute;
  219. right: 12px;
  220. top: 50%;
  221. transform: translateY(-50%);
  222. width: 16px;
  223. height: 16px;
  224. background-color: #999;
  225. border-radius: 50%;
  226. display: flex;
  227. align-items: center;
  228. justify-content: center;
  229. cursor: pointer;
  230. transition: background-color 0.2s;
  231. }
  232. .clear-btn:hover {
  233. background-color: #666;
  234. }
  235. .clear-x {
  236. position: relative;
  237. width: 8px;
  238. height: 8px;
  239. }
  240. .clear-x:before,
  241. .clear-x:after {
  242. content: '';
  243. position: absolute;
  244. width: 8px;
  245. height: 2px;
  246. background-color: white;
  247. top: 3px;
  248. left: 0;
  249. }
  250. .clear-x:before {
  251. transform: rotate(45deg);
  252. }
  253. .clear-x:after {
  254. transform: rotate(-45deg);
  255. }
  256. .FlexRow {
  257. display: flex;
  258. flex-direction: row;
  259. }
  260. .FlexColumn {
  261. display: flex;
  262. flex-direction: column;
  263. }
  264. </style>
  265. </head>
  266. <body class="container FlexRow">
  267. <div id="app" class="main00 FlexColumn">
  268. <div class="ListTop FlexRow">
  269. <div class="ListTop3 FlexRow">
  270. <div class="title">秒过加资料信息维护</div>
  271. </div>
  272. </div>
  273. <div class="main0 FlexRow">
  274. <!-- 左侧搜索面板 -->
  275. <div class="search-panel" style="position: relative;">
  276. <div v-if="isSearchLoading" class="loading-overlay">
  277. <div class="loading-spinner"></div>
  278. </div>
  279. <div class="search-box FlexRow">
  280. <div style="position: relative;">
  281. <input type="text" class="search-input" v-model="searchText" @keyup.enter="searchKeyword"
  282. placeholder="输入关键词搜索...">
  283. <div class="clear-btn" v-show="searchText" @click="clearSearch">
  284. <span class="clear-x"></span>
  285. </div>
  286. </div>
  287. <div class="btn33 FlexRow" @click="searchKeyword">
  288. <img title="搜索" alt="搜索"
  289. src="https://kylx365-1253256735.file.myqcloud.com/web/universalpic_search_gray_30x30.png"
  290. style="width: 20px; height: 20px;" />
  291. </div>
  292. </div>
  293. <!-- 搜索结果列表 -->
  294. <div class="search-results" style="flex: 1; overflow-y: auto; padding: 10px;">
  295. <div v-if="searchResults && searchResults.length > 0"
  296. v-for="(item, index) in searchResults"
  297. :key="index"
  298. @click="selectItem(item)"
  299. style="padding: 10px; margin: 5px 0; background: white; border-radius: 4px; cursor: pointer; transition: background-color 0.2s;"
  300. :style="{ backgroundColor: selectedItem === item ? '#e6f3ff' : 'white' }"
  301. class="search-result-item">
  302. <div v-if="typeof item === 'object'">
  303. <div style="font-weight: bold;">{{ item.Word || item }}</div>
  304. <div v-if="item.Author" style="font-size: 12px; color: #666; margin-top: 4px;">
  305. 作者: {{ item.Author }}
  306. </div>
  307. <div v-if="item.SearchType" style="font-size: 12px; color: #666;">
  308. 类型: {{ item.SearchType }}
  309. </div>
  310. <div v-if="item.ShiciUrl" style="font-size: 12px; color: #0066cc; text-decoration: underline;">
  311. {{ item.ShiciUrl }}
  312. </div>
  313. </div>
  314. <div v-else>{{ item }}</div>
  315. </div>
  316. <div v-else-if="searchResults && searchResults.length === 0"
  317. style="text-align: center; color: #999; padding: 20px;">
  318. 未找到相关结果
  319. </div>
  320. </div>
  321. </div>
  322. <!-- 右侧JSON编辑器 -->
  323. <div class="json-editor" v-if="jsonData" style="position: relative;">
  324. <div v-if="isDetailLoading" class="loading-overlay">
  325. <div class="loading-spinner"></div>
  326. </div>
  327. <div class="json-node" v-for="(value, key) in jsonData" :key="key">
  328. <span class="json-key">"{{ key }}":</span>
  329. <div v-if="typeof value === 'object' && value !== null" style="margin-left: 20px;">
  330. <div v-for="(subValue, subKey) in value" :key="subKey">
  331. <span class="json-key">"{{ subKey }}":</span>
  332. <!-- 处理第二层对象 -->
  333. <div v-if="typeof subValue === 'object' && subValue !== null && !Array.isArray(subValue)" style="margin-left: 20px;">
  334. <div v-for="(subSubValue, subSubKey) in subValue" :key="subSubKey">
  335. <span class="json-key">"{{ subSubKey }}":</span>
  336. <span v-if="typeof subSubValue === 'string'" class="json-string editable"
  337. @dblclick="editValue(subValue, subSubKey, key + '.' + subKey + '.' + subSubKey)">
  338. <template v-if="editingKey === key + '.' + subKey + '.' + subSubKey">
  339. <input type="text" class="json-editor-input" v-model="subValue[subSubKey]"
  340. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  341. </template>
  342. <template v-else>
  343. "{{ subSubValue }}"
  344. </template>
  345. </span>
  346. <span v-else-if="typeof subSubValue === 'number'" class="json-number editable"
  347. @dblclick="editValue(subValue, subSubKey, key + '.' + subKey + '.' + subSubKey)">
  348. <template v-if="editingKey === key + '.' + subKey + '.' + subSubKey">
  349. <input type="number" class="json-editor-input" v-model.number="subValue[subSubKey]"
  350. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  351. </template>
  352. <template v-else>
  353. {{ subSubValue }}
  354. </template>
  355. </span>
  356. <span v-else-if="typeof subSubValue === 'boolean'" class="json-boolean editable"
  357. @dblclick="editValue(subValue, subSubKey, key + '.' + subKey + '.' + subSubKey)">
  358. <template v-if="editingKey === key + '.' + subKey + '.' + subSubKey">
  359. <select class="json-editor-input" v-model="subValue[subSubKey]" @blur="finishEdit"
  360. @change="finishEdit" v-focus>
  361. <option :value="true">true</option>
  362. <option :value="false">false</option>
  363. </select>
  364. </template>
  365. <template v-else>
  366. {{ subSubValue }}
  367. </template>
  368. </span>
  369. <span v-else-if="subSubValue === null" class="json-null">null</span>
  370. <span v-else-if="typeof subSubValue === 'object'" class="json-string">
  371. [复杂对象]
  372. </span>
  373. </div>
  374. </div>
  375. <!-- 处理数组 -->
  376. <div v-else-if="Array.isArray(subValue)" style="margin-left: 20px;">
  377. <div v-for="(item, index) in subValue" :key="index">
  378. <span class="json-key">[{{ index }}]:</span>
  379. <span v-if="typeof item === 'string'" class="json-string editable"
  380. @dblclick="editValue(subValue, index, key + '.' + subKey + '.' + index)">
  381. <template v-if="editingKey === key + '.' + subKey + '.' + index">
  382. <input type="text" class="json-editor-input" v-model="subValue[index]"
  383. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  384. </template>
  385. <template v-else>
  386. <template v-if="['BiShunArr', 'BiShunArr2', 'KaitiArr'].includes(subKey)">
  387. <img :src="item"
  388. style="max-width: 100px; max-height: 100px; margin: 5px 0;"
  389. @error="handleImageError($event)"
  390. :alt="item" />
  391. <div class="json-string" style="font-size: 12px; color: #666;">"{{ item }}"</div>
  392. </template>
  393. <template v-else>
  394. "{{ item }}"
  395. </template>
  396. </template>
  397. </span>
  398. <span v-else-if="typeof item === 'number'" class="json-number editable"
  399. @dblclick="editValue(subValue, index, key + '.' + subKey + '.' + index)">
  400. <template v-if="editingKey === key + '.' + subKey + '.' + index">
  401. <input type="number" class="json-editor-input" v-model.number="subValue[index]"
  402. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  403. </template>
  404. <template v-else>
  405. {{ item }}
  406. </template>
  407. </span>
  408. <span v-else-if="typeof item === 'boolean'" class="json-boolean editable"
  409. @dblclick="editValue(subValue, index, key + '.' + subKey + '.' + index)">
  410. <template v-if="editingKey === key + '.' + subKey + '.' + index">
  411. <select class="json-editor-input" v-model="subValue[index]" @blur="finishEdit"
  412. @change="finishEdit" v-focus>
  413. <option :value="true">true</option>
  414. <option :value="false">false</option>
  415. </select>
  416. </template>
  417. <template v-else>
  418. {{ item }}
  419. </template>
  420. </span>
  421. <span v-else-if="item === null" class="json-null">null</span>
  422. <div v-else-if="typeof item === 'object'" style="margin-left: 20px;">
  423. <div v-for="(itemValue, itemKey) in item" :key="itemKey">
  424. <span class="json-key">"{{ itemKey }}":</span>
  425. <span v-if="typeof itemValue === 'string'" class="json-string editable"
  426. @dblclick="editValue(item, itemKey, key + '.' + subKey + '.' + index + '.' + itemKey)">
  427. <template v-if="editingKey === key + '.' + subKey + '.' + index + '.' + itemKey">
  428. <input type="text" class="json-editor-input" v-model="item[itemKey]"
  429. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  430. </template>
  431. <template v-else>
  432. "{{ itemValue }}"
  433. </template>
  434. </span>
  435. <span v-else-if="typeof itemValue === 'number'" class="json-number editable"
  436. @dblclick="editValue(item, itemKey, key + '.' + subKey + '.' + index + '.' + itemKey)">
  437. <template v-if="editingKey === key + '.' + subKey + '.' + index + '.' + itemKey">
  438. <input type="number" class="json-editor-input" v-model.number="item[itemKey]"
  439. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  440. </template>
  441. <template v-else>
  442. {{ itemValue }}
  443. </template>
  444. </span>
  445. <div v-else-if="Array.isArray(itemValue)" style="margin-left: 20px;">
  446. <div v-for="(arrayItem, arrayIndex) in itemValue" :key="arrayIndex">
  447. <span class="json-key">[{{ arrayIndex }}]:</span>
  448. <span v-if="typeof arrayItem === 'string'" class="json-string editable"
  449. @dblclick="editValue(itemValue, arrayIndex, key + '.' + subKey + '.' + index + '.' + itemKey + '.' + arrayIndex)">
  450. <template v-if="editingKey === key + '.' + subKey + '.' + index + '.' + itemKey + '.' + arrayIndex">
  451. <input type="text" class="json-editor-input" v-model="itemValue[arrayIndex]"
  452. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  453. </template>
  454. <template v-else>
  455. "{{ arrayItem }}"
  456. </template>
  457. </span>
  458. <span v-else-if="typeof arrayItem === 'number'" class="json-number editable"
  459. @dblclick="editValue(itemValue, arrayIndex, key + '.' + subKey + '.' + index + '.' + itemKey + '.' + arrayIndex)">
  460. <template v-if="editingKey === key + '.' + subKey + '.' + index + '.' + itemKey + '.' + arrayIndex">
  461. <input type="number" class="json-editor-input" v-model.number="itemValue[arrayIndex]"
  462. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  463. </template>
  464. <template v-else>
  465. {{ arrayItem }}
  466. </template>
  467. </span>
  468. <span v-else-if="typeof arrayItem === 'boolean'" class="json-boolean editable"
  469. @dblclick="editValue(itemValue, arrayIndex, key + '.' + subKey + '.' + index + '.' + itemKey + '.' + arrayIndex)">
  470. <template v-if="editingKey === key + '.' + subKey + '.' + index + '.' + itemKey + '.' + arrayIndex">
  471. <select class="json-editor-input" v-model="itemValue[arrayIndex]" @blur="finishEdit"
  472. @change="finishEdit" v-focus>
  473. <option :value="true">true</option>
  474. <option :value="false">false</option>
  475. </select>
  476. </template>
  477. <template v-else>
  478. {{ arrayItem }}
  479. </template>
  480. </span>
  481. <span v-else-if="arrayItem === null" class="json-null">null</span>
  482. <span v-else-if="typeof arrayItem === 'object'" class="json-string">[复杂对象]</span>
  483. </div>
  484. </div>
  485. </div>
  486. </div>
  487. </div>
  488. </div>
  489. <!-- 处理基本类型 -->
  490. <span v-else-if="typeof subValue === 'string'" class="json-string editable"
  491. @dblclick="editValue(value, subKey, key + '.' + subKey)">
  492. <template v-if="editingKey === key + '.' + subKey">
  493. <input type="text" class="json-editor-input" v-model="value[subKey]"
  494. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  495. </template>
  496. <template v-else>
  497. "{{ subValue }}"
  498. </template>
  499. </span>
  500. <span v-else-if="typeof subValue === 'number'" class="json-number editable"
  501. @dblclick="editValue(value, subKey, key + '.' + subKey)">
  502. <template v-if="editingKey === key + '.' + subKey">
  503. <input type="number" class="json-editor-input" v-model.number="value[subKey]"
  504. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  505. </template>
  506. <template v-else>
  507. {{ subValue }}
  508. </template>
  509. </span>
  510. <span v-else-if="typeof subValue === 'boolean'" class="json-boolean editable"
  511. @dblclick="editValue(value, subKey, key + '.' + subKey)">
  512. <template v-if="editingKey === key + '.' + subKey">
  513. <select class="json-editor-input" v-model="value[subKey]" @blur="finishEdit"
  514. @change="finishEdit" v-focus>
  515. <option :value="true">true</option>
  516. <option :value="false">false</option>
  517. </select>
  518. </template>
  519. <template v-else>
  520. {{ subValue }}
  521. </template>
  522. </span>
  523. <span v-else-if="subValue === null" class="json-null">null</span>
  524. </div>
  525. </div>
  526. <span v-else-if="typeof value === 'string'" class="json-string editable"
  527. @dblclick="editValue(jsonData, key, key)">
  528. <template v-if="editingKey === key">
  529. <input type="text" class="json-editor-input" v-model="jsonData[key]" @blur="finishEdit"
  530. @keyup.enter="finishEdit" v-focus>
  531. </template>
  532. <template v-else>
  533. "{{ value }}"
  534. </template>
  535. </span>
  536. <span v-else-if="typeof value === 'number'" class="json-number editable"
  537. @dblclick="editValue(jsonData, key, key)">
  538. <template v-if="editingKey === key">
  539. <input type="number" class="json-editor-input" v-model.number="jsonData[key]"
  540. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  541. </template>
  542. <template v-else>
  543. {{ value }}
  544. </template>
  545. </span>
  546. <span v-else-if="typeof value === 'boolean'" class="json-boolean editable"
  547. @dblclick="editValue(jsonData, key, key)">
  548. <template v-if="editingKey === key">
  549. <select class="json-editor-input" v-model="jsonData[key]" @blur="finishEdit"
  550. @change="finishEdit" v-focus>
  551. <option :value="true">true</option>
  552. <option :value="false">false</option>
  553. </select>
  554. </template>
  555. <template v-else>
  556. {{ value }}
  557. </template>
  558. </span>
  559. <span v-else-if="value === null" class="json-null">null</span>
  560. </div>
  561. </div>
  562. <div v-else class="json-editor FlexColumn" style="justify-content: center; align-items: center;">
  563. <div style="font-size:36px;color:#999;">请输入关键词搜索</div>
  564. </div>
  565. </div>
  566. <!-- 保存按钮 -->
  567. <div class="btn-group" v-if="jsonData">
  568. <button type="button" class="btn btn-default" @click="resetData">重置</button>
  569. <button type="button" class="btn btn-primary" @click="saveData">保存</button>
  570. </div>
  571. <!-- Toast提示 -->
  572. <div class="toast" :class="{ show: showToast, [toastType]: showToast }">{{ toastMessage }}</div>
  573. </div>
  574. <script>
  575. // 自定义指令:自动聚焦
  576. Vue.directive('focus', {
  577. inserted: function (el) {
  578. el.focus()
  579. }
  580. })
  581. new Vue({
  582. el: '#app',
  583. data: {
  584. searchText: '',
  585. jsonData: null,
  586. originalData: null,
  587. editingKey: null,
  588. showToast: false,
  589. toastMessage: '',
  590. toastType: 'info',
  591. searchResults: null,
  592. selectedItem: null,
  593. isSearchLoading: false,
  594. isDetailLoading: false
  595. },
  596. methods: {
  597. // 清空搜索
  598. clearSearch() {
  599. this.searchText = '';
  600. this.jsonData = null;
  601. this.originalData = null;
  602. this.searchResults = null;
  603. this.selectedItem = null;
  604. },
  605. // 搜索关键词
  606. searchKeyword() {
  607. if (!this.searchText.trim()) {
  608. this.showToastMessage('请输入搜索关键词', 'info');
  609. return;
  610. }
  611. this.isSearchLoading = true;
  612. fetch(`/api/GetMiaoguoLiteracyListByWord?Word=${encodeURIComponent(this.searchText)}`)
  613. .then(response => response.json())
  614. .then(data => {
  615. data = data.result;
  616. if (data && Array.isArray(data)) {
  617. // 对搜索结果进行三级排序
  618. data.sort((a, b) => {
  619. // 检查是否是对象
  620. const aIsObj = typeof a === 'object' && a !== null;
  621. const bIsObj = typeof b === 'object' && b !== null;
  622. // 获取Word值
  623. const aWord = aIsObj ? a.Word : a;
  624. const bWord = bIsObj ? b.Word : b;
  625. // 检查是否是精确匹配(Word等于关键词且其他字段为空)
  626. const aIsExactMatch = aIsObj &&
  627. aWord === this.searchText &&
  628. (!a.Author || a.Author === '') &&
  629. (!a.SearchType || a.SearchType === '') &&
  630. (!a.ShiciUrl || a.ShiciUrl === '');
  631. const bIsExactMatch = bIsObj &&
  632. bWord === this.searchText &&
  633. (!b.Author || b.Author === '') &&
  634. (!b.SearchType || b.SearchType === '') &&
  635. (!b.ShiciUrl || b.ShiciUrl === '');
  636. // 检查是否是Word匹配但有其他数据
  637. const aIsPartialMatch = aIsObj &&
  638. aWord === this.searchText &&
  639. !aIsExactMatch;
  640. const bIsPartialMatch = bIsObj &&
  641. bWord === this.searchText &&
  642. !bIsExactMatch;
  643. // 三级排序逻辑
  644. if (aIsExactMatch !== bIsExactMatch) {
  645. // 第一优先级:精确匹配(Word等于关键词且其他字段为空)
  646. return aIsExactMatch ? -1 : 1;
  647. } else if (aIsPartialMatch !== bIsPartialMatch) {
  648. // 第二优先级:Word等于关键词但其他字段有数据
  649. return aIsPartialMatch ? -1 : 1;
  650. } else if (aWord === this.searchText && bWord !== this.searchText) {
  651. // 第三优先级:Word等于关键词 vs 不等于
  652. return -1;
  653. } else if (aWord !== this.searchText && bWord === this.searchText) {
  654. return 1;
  655. }
  656. // 其他情况保持原有顺序
  657. return 0;
  658. });
  659. this.searchResults = data;
  660. this.selectedItem = null;
  661. this.jsonData = null; // 清空右侧详情
  662. this.originalData = null;
  663. } else {
  664. this.searchResults = [];
  665. this.showToastMessage('未找到相关结果', 'info');
  666. }
  667. this.isSearchLoading = false;
  668. })
  669. .catch(error => {
  670. console.error('搜索出错:', error);
  671. this.showToastMessage('搜索失败,请稍后重试', 'error');
  672. this.isSearchLoading = false;
  673. });
  674. },
  675. // 选择列表项
  676. selectItem(item) {
  677. this.selectedItem = item;
  678. this.isDetailLoading = true;
  679. fetch(`/api/GetMiaoguoLiteracyListByID?ID=${this.selectedItem.ID}`)
  680. .then(response => response.text())
  681. .then(text => {
  682. // 修复JSON格式问题,添加缺失的逗号
  683. const fixedText = this.fixJsonFormat(text);
  684. const data = JSON.parse(fixedText);
  685. var json = data.result;
  686. this.jsonData = json;
  687. this.originalData = JSON.parse(JSON.stringify(json)); // 深拷贝
  688. this.isDetailLoading = false;
  689. })
  690. .catch(error => {
  691. console.error('获取详情出错:', error);
  692. this.showToastMessage('获取详情失败,请稍后重试', 'error');
  693. this.isDetailLoading = false;
  694. });
  695. },
  696. // 编辑值
  697. editValue(obj, key, path) {
  698. this.editingKey = path || key;
  699. },
  700. // 完成编辑
  701. finishEdit() {
  702. this.editingKey = null;
  703. },
  704. // 重置数据
  705. resetData() {
  706. if (this.originalData) {
  707. this.jsonData = JSON.parse(JSON.stringify(this.originalData));
  708. }
  709. },
  710. // 保存数据
  711. saveData() {
  712. fetch('/api/UpdateMiaoguoLiteracyByWord', {
  713. method: 'POST',
  714. headers: {
  715. 'Content-Type': 'application/json'
  716. },
  717. body: JSON.stringify({
  718. ID: this.selectedItem.ID,
  719. JSONString: JSON.stringify(this.jsonData)
  720. })
  721. })
  722. .then(response => response.json())
  723. .then(data => {
  724. if (data) {
  725. this.showToastMessage('保存成功', 'success');
  726. this.originalData = JSON.parse(JSON.stringify(this.jsonData));
  727. } else {
  728. this.showToastMessage('保存失败', 'error');
  729. }
  730. })
  731. .catch(error => {
  732. console.error('保存出错:', error);
  733. this.showToastMessage('保存失败,请稍后重试', 'error');
  734. });
  735. },
  736. // 显示Toast消息
  737. showToastMessage(message, type = 'info') {
  738. this.toastMessage = message;
  739. this.toastType = type;
  740. this.showToast = true;
  741. setTimeout(() => {
  742. this.showToast = false;
  743. }, 3000);
  744. },
  745. // 修复JSON格式问题
  746. fixJsonFormat(jsonStr) {
  747. // 修复缺少逗号的问题
  748. // 在引号后面跟着引号的地方添加逗号
  749. let fixed = jsonStr.replace(/(["\d])\s*"([^"]+)":/g, '$1,"$2":');
  750. // 修复数字后面跟着引号的情况
  751. fixed = fixed.replace(/(\d+)\s*"([^"]+)":/g, '$1,"$2":');
  752. // 修复大括号后面跟着引号的情况
  753. fixed = fixed.replace(/({)\s*"([^"]+)":/g, '$1"$2":');
  754. // 修复方括号后面跟着引号的情况
  755. fixed = fixed.replace(/(\[)\s*"([^"]+)":/g, '$1"$2":');
  756. //console.log("原始JSON:", jsonStr);
  757. //console.log("修复后JSON:", fixed);
  758. return fixed;
  759. },
  760. // 处理图片加载错误
  761. handleImageError(event) {
  762. // 添加错误样式,显示边框和错误提示
  763. event.target.style.border = '1px dashed #ff6b6b';
  764. event.target.style.padding = '10px';
  765. event.target.style.backgroundColor = '#fff0f0';
  766. // 如果是gif图片,尝试使用代理或CORS解决方案
  767. if (event.target.src.endsWith('.gif')) {
  768. // 可以尝试使用代理服务器来解决CORS问题
  769. // 这里我们只添加一个提示,实际项目中可以根据需要实现代理
  770. const originalSrc = event.target.src;
  771. // 创建一个错误提示元素
  772. const errorDiv = document.createElement('div');
  773. errorDiv.style.color = '#ff6b6b';
  774. errorDiv.style.fontSize = '12px';
  775. errorDiv.style.marginTop = '5px';
  776. errorDiv.textContent = 'GIF图片加载失败,可能存在跨域问题';
  777. // 将错误提示添加到图片后面
  778. event.target.parentNode.insertBefore(errorDiv, event.target.nextSibling);
  779. }
  780. }
  781. }
  782. });
  783. </script>
  784. </body>
  785. </html>