yjbdc_articles.html 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  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>YJBDC文章维护</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% {
  215. transform: rotate(0deg);
  216. }
  217. 100% {
  218. transform: rotate(360deg);
  219. }
  220. }
  221. .clear-btn {
  222. position: absolute;
  223. right: 12px;
  224. top: 50%;
  225. transform: translateY(-50%);
  226. width: 16px;
  227. height: 16px;
  228. background-color: #999;
  229. border-radius: 50%;
  230. display: flex;
  231. align-items: center;
  232. justify-content: center;
  233. cursor: pointer;
  234. transition: background-color 0.2s;
  235. }
  236. .clear-btn:hover {
  237. background-color: #666;
  238. }
  239. .clear-x {
  240. position: relative;
  241. width: 8px;
  242. height: 8px;
  243. }
  244. .clear-x:before,
  245. .clear-x:after {
  246. content: '';
  247. position: absolute;
  248. width: 8px;
  249. height: 2px;
  250. background-color: white;
  251. top: 3px;
  252. left: 0;
  253. }
  254. .clear-x:before {
  255. transform: rotate(45deg);
  256. }
  257. .clear-x:after {
  258. transform: rotate(-45deg);
  259. }
  260. .FlexRow {
  261. display: flex;
  262. flex-direction: row;
  263. }
  264. .FlexColumn {
  265. display: flex;
  266. flex-direction: column;
  267. }
  268. </style>
  269. </head>
  270. <body class="container FlexRow">
  271. <div id="app" class="main00 FlexColumn">
  272. <div class="ListTop FlexRow">
  273. <div class="ListTop3 FlexRow">
  274. <div class="title">YJBDC文章维护</div>
  275. </div>
  276. </div>
  277. <div class="main0 FlexRow">
  278. <!-- 左侧搜索面板 -->
  279. <div class="search-panel" style="position: relative;">
  280. <div v-if="isSearchLoading" class="loading-overlay">
  281. <div class="loading-spinner"></div>
  282. </div>
  283. <div class="search-box FlexRow">
  284. <div style="position: relative;">
  285. <input type="text" class="search-input" v-model="searchText" @keyup.enter="searchArticles"
  286. placeholder="输入用户ID搜索...">
  287. <div class="clear-btn" v-show="searchText" @click="clearSearch">
  288. <span class="clear-x"></span>
  289. </div>
  290. </div>
  291. <div class="btn33 FlexRow" @click="searchArticles">
  292. <img title="搜索" alt="搜索"
  293. src="https://kylx365-1253256735.file.myqcloud.com/web/universalpic_search_gray_30x30.png"
  294. style="width: 20px; height: 20px;" />
  295. </div>
  296. </div>
  297. <!-- 搜索结果列表 -->
  298. <div class="search-results" style="flex: 1; overflow-y: auto; padding: 10px;">
  299. <div v-if="articles && articles.length > 0" v-for="(article, index) in articles" :key="index"
  300. @click="selectArticle(article)"
  301. style="padding: 10px; margin: 5px 0; background: white; border-radius: 4px; cursor: pointer; transition: background-color 0.2s;"
  302. :style="{ backgroundColor: selectedArticle === article ? '#e6f3ff' : 'white' }"
  303. class="search-result-item">
  304. <div style="font-weight: bold;">文章ID: {{ article.ID }}</div>
  305. <div style="font-size: 12px; color: #666; margin-top: 4px;">
  306. 创建时间: {{ article.CreateTime }}
  307. </div>
  308. <div style="font-size: 12px; color: #666;">
  309. 标题: {{ article.ArticleTitle }}
  310. </div>
  311. <div style="font-size: 12px; color: #666;">
  312. 单词表: {{ article.Words }}
  313. </div>
  314. <div style="font-size: 12px; color: #666;">
  315. 是否精选: {{ article.IsFine ? "是" : "否" }}
  316. </div>
  317. </div>
  318. <div v-else-if="articles && articles.length === 0"
  319. style="text-align: center; color: #999; padding: 20px;">
  320. 未找到相关文章
  321. </div>
  322. </div>
  323. </div>
  324. <!-- 右侧文章内容编辑器 -->
  325. <div class="json-editor" v-if="selectedArticle" style="position: relative;">
  326. <div v-if="isDetailLoading" class="loading-overlay">
  327. <div class="loading-spinner"></div>
  328. </div>
  329. <div class="json-node">
  330. <span class="json-key">文章ID:</span>
  331. <span class="json-number">{{ selectedArticle.ID }}</span>
  332. </div>
  333. <div class="json-node">
  334. <span class="json-key">是否精选:</span>
  335. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'IsFine', 'IsFine')">
  336. <template v-if="editingKey === 'IsFine'">
  337. <label style="margin-right: 10px;">
  338. <input type="radio" v-model.number="selectedArticle.IsFine" :value="1"
  339. @change="finishEdit"> 是
  340. </label>
  341. <label>
  342. <input type="radio" v-model.number="selectedArticle.IsFine" :value="0"
  343. @change="finishEdit"> 否
  344. </label>
  345. </template>
  346. <template v-else>
  347. {{ selectedArticle.IsFine == 1 || selectedArticle.IsFine == "1" ? "是" : "否" }}
  348. </template>
  349. </span>
  350. </div>
  351. <div class="json-node">
  352. <span class="json-key">文章英文标题:</span>
  353. <span class="json-string editable"
  354. @dblclick="editValue(selectedArticle, 'ArticleTitle', 'ArticleTitle')">
  355. <template v-if="editingKey === 'ArticleTitle'">
  356. <input type="text" class="json-editor-input" v-model="selectedArticle.ArticleTitle"
  357. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  358. </template>
  359. <template v-else>
  360. "{{ selectedArticle.ArticleTitle }}"
  361. </template>
  362. </span>
  363. </div>
  364. <div class="json-node">
  365. <span class="json-key">文章中文标题:</span>
  366. <span class="json-string editable"
  367. @dblclick="editValue(selectedArticle, 'ArticleTitleCHN', 'ArticleTitleCHN')">
  368. <template v-if="editingKey === 'ArticleTitleCHN'">
  369. <input type="text" class="json-editor-input" v-model="selectedArticle.ArticleTitleCHN"
  370. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  371. </template>
  372. <template v-else>
  373. "{{ selectedArticle.ArticleTitleCHN }}"
  374. </template>
  375. </span>
  376. </div>
  377. <div class="json-node">
  378. <span class="json-key">文章图片名称:</span>
  379. <span class="json-string editable"
  380. @dblclick="editValue(selectedArticle, 'ArticleImage', 'ArticleImage')">
  381. <template v-if="editingKey === 'ArticleImage'">
  382. <input type="text" class="json-editor-input" v-model="selectedArticle.ArticleImage"
  383. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  384. </template>
  385. <template v-else>
  386. "{{ selectedArticle.ArticleImage }}"
  387. </template>
  388. </span>
  389. </div>
  390. <div class="json-node">
  391. <span class="json-key">创建时间:</span>
  392. <span class="json-string editable"
  393. @dblclick="editValue(selectedArticle, 'CreateTime', 'CreateTime')">
  394. <template v-if="editingKey === 'CreateTime'">
  395. <input type="text" disabled="true" class="json-editor-input"
  396. v-model="selectedArticle.CreateTime" @blur="finishEdit" @keyup.enter="finishEdit"
  397. v-focus>
  398. </template>
  399. <template v-else>
  400. "{{ selectedArticle.CreateTime }}"
  401. </template>
  402. </span>
  403. </div>
  404. <div class="json-node">
  405. <span class="json-key">英语文章:</span>
  406. <span class="json-string editable"
  407. @dblclick="editValue(selectedArticle, 'ArticleStart', 'ArticleStart')">
  408. <template v-if="editingKey === 'ArticleStart'">
  409. <textarea class="json-editor-input" rows="4"
  410. v-model="selectedArticle.ArticleStart" @blur="finishEdit" @keyup.enter="finishEdit"
  411. v-focus></textarea>
  412. </template>
  413. <template v-else>
  414. "{{ selectedArticle.ArticleStart }}"
  415. </template>
  416. </span>
  417. </div>
  418. <div class="json-node">
  419. <span class="json-key">用户ID:</span>
  420. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'UserID', 'UserID')">
  421. <template v-if="editingKey === 'UserID'">
  422. <input type="number" disabled="true" class="json-editor-input"
  423. v-model.number="selectedArticle.UserID" @blur="finishEdit" @keyup.enter="finishEdit"
  424. v-focus>
  425. </template>
  426. <template v-else>
  427. {{ selectedArticle.UserID }}
  428. </template>
  429. </span>
  430. </div>
  431. <div class="json-node">
  432. <span class="json-key">生成秒数:</span>
  433. <span class="json-number editable"
  434. @dblclick="editValue(selectedArticle, 'GenerateTime', 'GenerateTime')">
  435. <template v-if="editingKey === 'GenerateTime'">
  436. <input type="number" disabled="true" class="json-editor-input"
  437. v-model.number="selectedArticle.GenerateTime" @blur="finishEdit"
  438. @keyup.enter="finishEdit" v-focus>
  439. </template>
  440. <template v-else>
  441. {{ selectedArticle.GenerateTime }}
  442. </template>
  443. </span>
  444. </div>
  445. <div class="json-node">
  446. <span class="json-key">使用AI模型:</span>
  447. <span class="json-string editable"
  448. @dblclick="editValue(selectedArticle, 'AIProvider', 'AIProvider')">
  449. <template v-if="editingKey === 'AIProvider'">
  450. <input type="text" disabled="true" class="json-editor-input"
  451. v-model="selectedArticle.AIProvider" @blur="finishEdit" @keyup.enter="finishEdit"
  452. v-focus>
  453. </template>
  454. <template v-else>
  455. "{{ selectedArticle.AIProvider }}"
  456. </template>
  457. </span>
  458. </div>
  459. <div class="json-node">
  460. <span class="json-key">提示语:</span>
  461. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'BuildStr', 'BuildStr')">
  462. <template v-if="editingKey === 'BuildStr'">
  463. <input type="text" disabled="true" class="json-editor-input"
  464. v-model="selectedArticle.BuildStr" @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  465. </template>
  466. <template v-else>
  467. "{{ selectedArticle.BuildStr }}"
  468. </template>
  469. </span>
  470. </div>
  471. <div class="json-node">
  472. <span class="json-key">单词表:</span>
  473. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'Words', 'Words')">
  474. <template v-if="editingKey === 'Words'">
  475. <input type="text" class="json-editor-input" v-model="selectedArticle.Words"
  476. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  477. </template>
  478. <template v-else>
  479. "{{ selectedArticle.Words }}"
  480. </template>
  481. </span>
  482. </div>
  483. <div class="json-node">
  484. <span class="json-key">文章难度:</span>
  485. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'Level', 'Level')">
  486. <template v-if="editingKey === 'Level'">
  487. <input type="text" disabled="true" class="json-editor-input" v-model="selectedArticle.Level"
  488. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  489. </template>
  490. <template v-else>
  491. "{{ selectedArticle.Level }} {{ getLevelName(selectedArticle.Level) }}"
  492. </template>
  493. </span>
  494. </div>
  495. <div class="json-node">
  496. <span class="json-key">文章类型:</span>
  497. <span class="json-string editable"
  498. @dblclick="editValue(selectedArticle, 'ArticleStyle', 'ArticleStyle')">
  499. <template v-if="editingKey === 'ArticleStyle'">
  500. <input type="text" disabled="true" class="json-editor-input"
  501. v-model="selectedArticle.ArticleStyle" @blur="finishEdit" @keyup.enter="finishEdit"
  502. v-focus>
  503. </template>
  504. <template v-else>
  505. "{{ selectedArticle.ArticleStyle }}"
  506. </template>
  507. </span>
  508. </div>
  509. <div class="json-node">
  510. <span class="json-key">用户阅读数:</span>
  511. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'ReadCount', 'ReadCount')">
  512. <template v-if="editingKey === 'ReadCount'">
  513. <input type="number" disabled="true" class="json-editor-input"
  514. v-model.number="selectedArticle.ReadCount" @blur="finishEdit" @keyup.enter="finishEdit"
  515. v-focus>
  516. </template>
  517. <template v-else>
  518. {{ selectedArticle.ReadCount }}
  519. </template>
  520. </span>
  521. </div>
  522. <div class="json-node">
  523. <span class="json-key">JSON结果:</span>
  524. <span class="json-string editable"
  525. @dblclick="editValue(selectedArticle, 'JSONString', 'JSONString')">
  526. <template v-if="editingKey === 'JSONString'">
  527. <textarea class="json-editor-input" rows="20" v-model="selectedArticle.JSONString"
  528. @blur="finishEdit" @keyup.enter="finishEdit" v-focus></textarea>
  529. </template>
  530. <template v-else>
  531. "{{ selectedArticle.JSONString }}"
  532. </template>
  533. </span>
  534. </div>
  535. <div class="json-node">
  536. <span class="json-key">是否删除:</span>
  537. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'Flag', 'Flag')">
  538. <template v-if="editingKey === 'Flag'">
  539. <input type="number" disabled="true" class="json-editor-input"
  540. v-model.number="selectedArticle.Flag" @blur="finishEdit" @keyup.enter="finishEdit"
  541. v-focus>
  542. </template>
  543. <template v-else>
  544. {{ selectedArticle.Flag }}
  545. </template>
  546. </span>
  547. </div>
  548. <div class="btn-group">
  549. <button class="btn btn-primary" @click="saveArticle">保存</button>
  550. <button class="btn btn-default" @click="cancelEdit">取消</button>
  551. </div>
  552. </div>
  553. </div>
  554. <div class="toast" :class="{ show: showToastFlag, [toastType]: showToastFlag }">{{ toastMessage }}</div>
  555. </div>
  556. <script>
  557. new Vue({
  558. el: '#app',
  559. data: {
  560. searchText: '',
  561. articles: [],
  562. selectedArticle: null,
  563. isSearchLoading: false,
  564. isDetailLoading: false,
  565. editingKey: null,
  566. toastMessage: '',
  567. toastType: 'info',
  568. showToastFlag: false
  569. },
  570. methods: {
  571. searchArticles() {
  572. if (!this.searchText) return;
  573. this.isSearchLoading = true;
  574. $.ajax({
  575. url: `api/GetYJBDCArticleList?UserID=${this.searchText}&PageCount=100`,
  576. method: 'GET',
  577. success: (data) => {
  578. this.articles = data.result;
  579. this.isSearchLoading = false;
  580. },
  581. error: () => {
  582. this.isSearchLoading = false;
  583. }
  584. });
  585. },
  586. selectArticle(article) {
  587. this.isDetailLoading = true;
  588. this.selectedArticle = null;
  589. $.ajax({
  590. url: `api/GetYJBDCArticleList?UserID=${this.searchText}&ID=${article.ID}`,
  591. method: 'GET',
  592. success: (data) => {
  593. this.selectedArticle = data.result[0];
  594. this.isDetailLoading = false;
  595. },
  596. error: () => {
  597. this.isDetailLoading = false;
  598. }
  599. });
  600. },
  601. clearSearch() {
  602. this.searchText = '';
  603. this.articles = [];
  604. this.selectedArticle = null;
  605. },
  606. editValue(obj, key, editingKey) {
  607. // 如果是编辑JSON字符串,则格式化显示
  608. if (key === 'JSONString' && obj[key]) {
  609. try {
  610. // 先设置编辑状态,确保文本框显示
  611. this.editingKey = editingKey;
  612. // 延迟执行格式化,确保文本框已渲染
  613. this.$nextTick(() => {
  614. try {
  615. // 尝试解析JSON并格式化
  616. let jsonObj;
  617. try {
  618. // 尝试双重解析(处理可能被双重编码的情况)
  619. jsonObj = JSON.parse(JSON.parse(obj[key]));
  620. } catch (err) {
  621. // 如果双重解析失败,尝试单次解析
  622. jsonObj = JSON.parse(obj[key]);
  623. }
  624. // 使用Vue的$set方法确保响应式更新
  625. this.$set(obj, key, JSON.stringify(jsonObj, null, 4));
  626. } catch (e) {
  627. // 如果解析失败,保持原样
  628. console.error('JSON解析失败:', e);
  629. }
  630. });
  631. } catch (e) {
  632. // 如果解析失败,保持原样
  633. console.error('JSON解析失败:', e);
  634. this.editingKey = editingKey;
  635. }
  636. } else {
  637. // 非JSON字段直接设置编辑状态
  638. this.editingKey = editingKey;
  639. }
  640. },
  641. finishEdit() {
  642. // 如果是编辑JSON字符串,则在完成编辑时转回紧凑格式
  643. if (this.editingKey === 'JSONString' && this.selectedArticle && this.selectedArticle.JSONString) {
  644. try {
  645. // 尝试解析格式化的JSON并转回紧凑格式
  646. const jsonObj = JSON.parse(this.selectedArticle.JSONString);
  647. // 使用Vue的$set方法确保响应式更新
  648. // 保存时需要确保JSON字符串被正确转义
  649. this.$set(this.selectedArticle, 'JSONString', JSON.stringify(jsonObj));
  650. } catch (e) {
  651. // 如果解析失败,保持原样
  652. console.error('JSON解析失败:', e);
  653. }
  654. }
  655. this.editingKey = null;
  656. },
  657. saveArticle() {
  658. if (!this.selectedArticle) return;
  659. this.isDetailLoading = true;
  660. this.selectedArticle.Source = "web";
  661. // 确保JSONString是紧凑格式
  662. if (this.selectedArticle.JSONString) {
  663. try {
  664. const jsonObj = JSON.parse(this.selectedArticle.JSONString);
  665. // 使用Vue的$set方法确保响应式更新
  666. // 保存时需要确保JSON字符串被正确转义
  667. this.$set(this.selectedArticle, 'JSONString', JSON.stringify(jsonObj));
  668. } catch (e) {
  669. console.error('JSON解析失败:', e);
  670. // 如果JSON格式不正确,提示用户并中止保存
  671. this.isDetailLoading = false;
  672. this.showToast('JSON格式不正确,请检查后重试', 'error');
  673. return;
  674. }
  675. }
  676. $.ajax({
  677. url: 'api/UpdateYJBDCArticle',
  678. method: 'POST',
  679. data: this.selectedArticle,
  680. success: () => {
  681. this.isDetailLoading = false;
  682. this.showToast('保存成功', 'success');
  683. // 重新加载文章列表和当前选中的文章
  684. this.searchArticles();
  685. this.selectArticle(this.selectedArticle);
  686. },
  687. error: () => {
  688. this.isDetailLoading = false;
  689. alert('保存失败');
  690. }
  691. });
  692. },
  693. cancelEdit() {
  694. this.selectedArticle = null;
  695. },
  696. showToast(message, type = 'info') {
  697. this.toastMessage = message;
  698. this.toastType = type;
  699. this.showToastFlag = true;
  700. // 自动隐藏
  701. setTimeout(() => {
  702. this.showToastFlag = false;
  703. }, 3000);
  704. },
  705. getLevelName(level){
  706. let result="";
  707. switch(level){
  708. case "0":
  709. result="A1";
  710. break;
  711. case "1":
  712. result="A2";
  713. break;
  714. case "2":
  715. result="B1";
  716. break;
  717. case "3":
  718. result="B2";
  719. break;
  720. case "4":
  721. result="C1";
  722. break;
  723. case "5":
  724. result="C2";
  725. break;
  726. }
  727. return result;
  728. },
  729. },
  730. directives: {
  731. focus: {
  732. inserted(el) {
  733. el.focus();
  734. }
  735. }
  736. }
  737. });
  738. </script>
  739. </body>
  740. </html>