yjbdc_articles.html 25 KB

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