yjbdc_articles.html 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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% { 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">YJBDC文章维护</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="searchArticles"
  282. placeholder="输入用户ID搜索...">
  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="searchArticles">
  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="articles && articles.length > 0"
  296. v-for="(article, index) in articles"
  297. :key="index"
  298. @click="selectArticle(article)"
  299. style="padding: 10px; margin: 5px 0; background: white; border-radius: 4px; cursor: pointer; transition: background-color 0.2s;"
  300. :style="{ backgroundColor: selectedArticle === article ? '#e6f3ff' : 'white' }"
  301. class="search-result-item">
  302. <div style="font-weight: bold;">文章ID: {{ article.ID }}</div>
  303. <div style="font-size: 12px; color: #666; margin-top: 4px;">
  304. 创建时间: {{ article.CreateTime }}
  305. </div>
  306. <div style="font-size: 12px; color: #666;">
  307. 标题: {{ article.ArticleTitle }}
  308. </div>
  309. <div style="font-size: 12px; color: #666;">
  310. 单词表: {{ article.Words }}
  311. </div>
  312. <div style="font-size: 12px; color: #666;">
  313. 是否精选: {{ article.IsFine=="1"?"是":"" }}
  314. </div>
  315. </div>
  316. <div v-else-if="articles && articles.length === 0"
  317. style="text-align: center; color: #999; padding: 20px;">
  318. 未找到相关文章
  319. </div>
  320. </div>
  321. </div>
  322. <!-- 右侧文章内容编辑器 -->
  323. <div class="json-editor" v-if="selectedArticle" style="position: relative;">
  324. <div v-if="isDetailLoading" class="loading-overlay">
  325. <div class="loading-spinner"></div>
  326. </div>
  327. <div class="json-node">
  328. <span class="json-key">文章ID:</span>
  329. <span class="json-number">{{ selectedArticle.ID }}</span>
  330. </div>
  331. <div class="json-node">
  332. <span class="json-key">是否精选:</span>
  333. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'IsFine', 'IsFine')">
  334. <template v-if="editingKey === 'IsFine'">
  335. <input type="number" class="json-editor-input" v-model.number="selectedArticle.IsFine"
  336. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  337. </template>
  338. <template v-else>
  339. {{ selectedArticle.IsFine }}
  340. </template>
  341. </span>
  342. </div>
  343. <div class="json-node">
  344. <span class="json-key">文章英文标题:</span>
  345. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'ArticleTitle', 'ArticleTitle')">
  346. <template v-if="editingKey === 'ArticleTitle'">
  347. <input type="text" class="json-editor-input" v-model="selectedArticle.ArticleTitle"
  348. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  349. </template>
  350. <template v-else>
  351. "{{ selectedArticle.ArticleTitle }}"
  352. </template>
  353. </span>
  354. </div>
  355. <div class="json-node">
  356. <span class="json-key">文章中文标题:</span>
  357. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'ArticleTitleCHN', 'ArticleTitleCHN')">
  358. <template v-if="editingKey === 'ArticleTitleCHN'">
  359. <input type="text" class="json-editor-input" v-model="selectedArticle.ArticleTitleCHN"
  360. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  361. </template>
  362. <template v-else>
  363. "{{ selectedArticle.ArticleTitleCHN }}"
  364. </template>
  365. </span>
  366. </div>
  367. <div class="json-node">
  368. <span class="json-key">文章图片名称:</span>
  369. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'ArticleImage', 'ArticleImage')">
  370. <template v-if="editingKey === 'ArticleImage'">
  371. <input type="text" class="json-editor-input" v-model="selectedArticle.ArticleImage"
  372. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  373. </template>
  374. <template v-else>
  375. "{{ selectedArticle.ArticleImage }}"
  376. </template>
  377. </span>
  378. </div>
  379. <div class="json-node">
  380. <span class="json-key">创建时间:</span>
  381. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'CreateTime', 'CreateTime')">
  382. <template v-if="editingKey === 'CreateTime'">
  383. <input type="text" disabled="true" class="json-editor-input" v-model="selectedArticle.CreateTime"
  384. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  385. </template>
  386. <template v-else>
  387. "{{ selectedArticle.CreateTime }}"
  388. </template>
  389. </span>
  390. </div>
  391. <div class="json-node">
  392. <span class="json-key">英语文章:</span>
  393. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'ArticleStart', 'ArticleStart')">
  394. <template v-if="editingKey === 'ArticleStart'">
  395. <input type="text" disabled="true" class="json-editor-input" v-model="selectedArticle.ArticleStart"
  396. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  397. </template>
  398. <template v-else>
  399. "{{ selectedArticle.ArticleStart }}"
  400. </template>
  401. </span>
  402. </div>
  403. <div class="json-node">
  404. <span class="json-key">用户ID:</span>
  405. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'UserID', 'UserID')">
  406. <template v-if="editingKey === 'UserID'">
  407. <input type="number" disabled="true" class="json-editor-input" v-model.number="selectedArticle.UserID"
  408. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  409. </template>
  410. <template v-else>
  411. {{ selectedArticle.UserID }}
  412. </template>
  413. </span>
  414. </div>
  415. <div class="json-node">
  416. <span class="json-key">生成秒数:</span>
  417. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'GenerateTime', 'GenerateTime')">
  418. <template v-if="editingKey === 'GenerateTime'">
  419. <input type="number" disabled="true" class="json-editor-input" v-model.number="selectedArticle.GenerateTime"
  420. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  421. </template>
  422. <template v-else>
  423. {{ selectedArticle.GenerateTime }}
  424. </template>
  425. </span>
  426. </div>
  427. <div class="json-node">
  428. <span class="json-key">使用AI模型:</span>
  429. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'AIProvider', 'AIProvider')">
  430. <template v-if="editingKey === 'AIProvider'">
  431. <input type="text" disabled="true" class="json-editor-input" v-model="selectedArticle.AIProvider"
  432. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  433. </template>
  434. <template v-else>
  435. "{{ selectedArticle.AIProvider }}"
  436. </template>
  437. </span>
  438. </div>
  439. <div class="json-node">
  440. <span class="json-key">提示语:</span>
  441. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'BuildStr', 'BuildStr')">
  442. <template v-if="editingKey === 'BuildStr'">
  443. <input type="text" disabled="true" class="json-editor-input" v-model="selectedArticle.BuildStr"
  444. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  445. </template>
  446. <template v-else>
  447. "{{ selectedArticle.BuildStr }}"
  448. </template>
  449. </span>
  450. </div>
  451. <div class="json-node">
  452. <span class="json-key">单词表:</span>
  453. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'Words', 'Words')">
  454. <template v-if="editingKey === 'Words'">
  455. <input type="text" disabled="true" class="json-editor-input" v-model="selectedArticle.Words"
  456. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  457. </template>
  458. <template v-else>
  459. "{{ selectedArticle.Words }}"
  460. </template>
  461. </span>
  462. </div>
  463. <div class="json-node">
  464. <span class="json-key">文章难度:</span>
  465. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'Level', 'Level')">
  466. <template v-if="editingKey === 'Level'">
  467. <input type="text" disabled="true" class="json-editor-input" v-model="selectedArticle.Level"
  468. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  469. </template>
  470. <template v-else>
  471. "{{ selectedArticle.Level }}"
  472. </template>
  473. </span>
  474. </div>
  475. <div class="json-node">
  476. <span class="json-key">文章类型:</span>
  477. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'ArticleStyle', 'ArticleStyle')">
  478. <template v-if="editingKey === 'ArticleStyle'">
  479. <input type="text" disabled="true" class="json-editor-input" v-model="selectedArticle.ArticleStyle"
  480. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  481. </template>
  482. <template v-else>
  483. "{{ selectedArticle.ArticleStyle }}"
  484. </template>
  485. </span>
  486. </div>
  487. <div class="json-node">
  488. <span class="json-key">用户阅读数:</span>
  489. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'ReadCount', 'ReadCount')">
  490. <template v-if="editingKey === 'ReadCount'">
  491. <input type="number" disabled="true" class="json-editor-input" v-model.number="selectedArticle.ReadCount"
  492. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  493. </template>
  494. <template v-else>
  495. {{ selectedArticle.ReadCount }}
  496. </template>
  497. </span>
  498. </div>
  499. <div class="json-node">
  500. <span class="json-key">JSON结果:</span>
  501. <span class="json-string editable" @dblclick="editValue(selectedArticle, 'JSONString', 'JSONString')">
  502. <template v-if="editingKey === 'JSONString'">
  503. <textarea class="json-editor-input" disabled="true" v-model="selectedArticle.JSONString"
  504. @blur="finishEdit" @keyup.enter="finishEdit" v-focus></textarea>
  505. </template>
  506. <template v-else>
  507. "{{ selectedArticle.JSONString }}"
  508. </template>
  509. </span>
  510. </div>
  511. <div class="json-node">
  512. <span class="json-key">是否删除:</span>
  513. <span class="json-number editable" @dblclick="editValue(selectedArticle, 'Flag', 'Flag')">
  514. <template v-if="editingKey === 'Flag'">
  515. <input type="number" disabled="true" class="json-editor-input" v-model.number="selectedArticle.Flag"
  516. @blur="finishEdit" @keyup.enter="finishEdit" v-focus>
  517. </template>
  518. <template v-else>
  519. {{ selectedArticle.Flag }}
  520. </template>
  521. </span>
  522. </div>
  523. <div class="btn-group">
  524. <button class="btn btn-primary" @click="saveArticle">保存</button>
  525. <button class="btn btn-default" @click="cancelEdit">取消</button>
  526. </div>
  527. </div>
  528. </div>
  529. </div>
  530. <script>
  531. new Vue({
  532. el: '#app',
  533. data: {
  534. searchText: '',
  535. articles: [],
  536. selectedArticle: null,
  537. isSearchLoading: false,
  538. isDetailLoading: false,
  539. editingKey: null
  540. },
  541. methods: {
  542. searchArticles() {
  543. if (!this.searchText) return;
  544. this.isSearchLoading = true;
  545. $.ajax({
  546. url: `api/GetYJBDCArticleList?UserID=${this.searchText}&PageCount=100`,
  547. method: 'GET',
  548. success: (data) => {
  549. this.articles = data.result;
  550. this.isSearchLoading = false;
  551. },
  552. error: () => {
  553. this.isSearchLoading = false;
  554. }
  555. });
  556. },
  557. selectArticle(article) {
  558. this.isDetailLoading = true;
  559. this.selectedArticle = null;
  560. $.ajax({
  561. url: `api/GetYJBDCArticleList?UserID=${this.searchText}&ID=${article.ID}`,
  562. method: 'GET',
  563. success: (data) => {
  564. this.selectedArticle = data.result[0];
  565. this.isDetailLoading = false;
  566. },
  567. error: () => {
  568. this.isDetailLoading = false;
  569. }
  570. });
  571. },
  572. clearSearch() {
  573. this.searchText = '';
  574. this.articles = [];
  575. this.selectedArticle = null;
  576. },
  577. editValue(obj, key, editingKey) {
  578. this.editingKey = editingKey;
  579. },
  580. finishEdit() {
  581. this.editingKey = null;
  582. },
  583. saveArticle() {
  584. if (!this.selectedArticle) return;
  585. this.isDetailLoading = true;
  586. this.selectedArticle.Source="web";
  587. $.ajax({
  588. url: 'api/UpdateYJBDCArticle',
  589. method: 'POST',
  590. data: this.selectedArticle,
  591. success: () => {
  592. this.isDetailLoading = false;
  593. alert('保存成功');
  594. this.searchArticles();
  595. },
  596. error: () => {
  597. this.isDetailLoading = false;
  598. alert('保存失败');
  599. }
  600. });
  601. },
  602. cancelEdit() {
  603. this.selectedArticle = null;
  604. }
  605. },
  606. directives: {
  607. focus: {
  608. inserted(el) {
  609. el.focus();
  610. }
  611. }
  612. }
  613. });
  614. </script>
  615. </body>
  616. </html>