You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.vue 21 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. <template>
  2. <div>
  3. <TopHeader :menu="-1"></TopHeader>
  4. <div class="ui container">
  5. <div class="title">您申请的项目将在2030科技项目页面展示</div>
  6. <div class="form-c" v-show="!selectTechPrj">
  7. <div class="form-wrap">
  8. <div class="form-header">申请展示项目</div>
  9. <div class="form-content">
  10. <div class="form-row">
  11. <div class="row-label required">申请项目存放于</div>
  12. <div class="row-content">
  13. <el-radio v-model="form.type" label="openi" @input="changeType()">启智社区</el-radio>
  14. <el-radio v-model="form.type" label="no-openi" @input="changeType()">非启智社区</el-radio>
  15. </div>
  16. </div>
  17. <div class="form-row" :class="form.url_err ? 'form-row-err' : ''">
  18. <div class="row-label required">现项目地址</div>
  19. <div class="row-content">
  20. <el-input size="medium" v-model="form.url" @input="changeUrl" class="can-err"
  21. placeholder='请输入现有项目的 HTTP(s) 或 Git " clone" URL,如:https://openi.pcl.ac.cn/OpenI/aiforge'></el-input>
  22. </div>
  23. </div>
  24. <div v-show="form.type == 'no-openi'">
  25. <div class="form-row" :class="form.alias_err ? 'form-row-err' : ''">
  26. <div class="row-label required baseline">启智项目名称</div>
  27. <div class="row-content">
  28. <div>
  29. <el-input size="medium" v-model="form.repo_alias" @input="changeAlias" class="can-err"></el-input>
  30. </div>
  31. <div class="tips">
  32. 请输入中文、字母、数字和-_ .,最多100个字符。
  33. </div>
  34. </div>
  35. </div>
  36. <div class="form-row" :class="form.name_err ? 'form-row-err' : ''">
  37. <div class="row-label required baseline">启智项目路径</div>
  38. <div class="row-content">
  39. <div class="reop-url-c">
  40. <el-select size="medium" class="owner-sel" v-model="form.uid" @change="changeOwner">
  41. <div slot="prefix" class="owner-item" style="height:100%;padding-left:10px">
  42. <img class="owner-img" :src="ownerSelect.RelAvatarLink">
  43. </div>
  44. <el-option v-for="item in ownerList" :key="item.value" :value="item.value" :label="item.label">
  45. <div class="owner-item">
  46. <img class="owner-img" :src="item.RelAvatarLink">
  47. <span class="owner-name">{{(item.FullName || item.Name)}}</span>
  48. </div>
  49. </el-option>
  50. </el-select>
  51. <span style="margin: 0 8px;font-size:22px;"> / </span>
  52. <el-input size="medium" v-model="form.repo_name" @input="changeRepoName" class="can-err"></el-input>
  53. </div>
  54. <div class="tips">
  55. 启智项目地址:<span class="openi-repo-url">{{ form.repo_url }}</span>
  56. </div>
  57. </div>
  58. </div>
  59. <div class="form-row">
  60. <div class="row-label">关键词</div>
  61. <div class="row-content">
  62. <el-select style="width:100%" size="medium" v-model="form.topics" multiple filterable remote
  63. allow-create default-first-option placeholder="请选择标签" :remote-method="searchTopics"
  64. :loading="form.topicLoading">
  65. <el-option v-for="item in topicsList" :key="item.value" :label="item.label" :value="item.value">
  66. </el-option>
  67. </el-select>
  68. </div>
  69. </div>
  70. <div class="form-row">
  71. <div class="row-label baseline">项目简介</div>
  72. <div class="row-content">
  73. <el-input size="medium" type="textarea" :rows="3" placeholder="请输入项目简介(长度不超过255)" :maxLength="255"
  74. v-model="form.description"></el-input>
  75. </div>
  76. </div>
  77. </div>
  78. <div class="form-row" :class="form.tech_err ? 'form-row-err' : ''">
  79. <div class="row-label required">科技项目</div>
  80. <div class="row-content">
  81. <el-input placeholder="请选择科技项目" size="medium" v-model="form.tech_show" :readonly="true" class="can-err">
  82. <el-button size="medium" slot="append" class="btn-select" @click="goSelectTechPrj">选择科技项目</el-button>
  83. <el-button size="medium" slot="append" class="btn-clear" @click="clearTechPrj">清除</el-button>
  84. </el-input>
  85. </div>
  86. </div>
  87. <div class="form-row" :class="form.institution_err ? 'form-row-err' : ''">
  88. <div class="row-label required">成果贡献单位</div>
  89. <div class="row-content">
  90. <el-select style="width:100%" size="medium" v-model="form.institution" multiple class="can-err">
  91. <el-option v-for="item in institutionList" :key="item.key" :label="item.value"
  92. :value="item.value"></el-option>
  93. </el-select>
  94. </div>
  95. </div>
  96. <div class="form-btn-group">
  97. <el-button size="medium" type="primary" :loading="submitLoading" class="btn confirm-btn"
  98. @click="submit">提交申请</el-button>
  99. <el-button size="medium" class="btn" @click="cancel">{{ $t('cancel') }}</el-button>
  100. </div>
  101. </div>
  102. </div>
  103. </div>
  104. <div class="form-select-tech-prj" v-show="selectTechPrj">
  105. <div class="form-wrap">
  106. <div class="form-header">
  107. <span>选择科技项目</span>
  108. <span>
  109. <el-button class="form-btn-go-back" @click="goBack">返回</el-button>
  110. </span>
  111. </div>
  112. <div class="form-content">
  113. <div class="form-row">
  114. <div class="row-label">请输入科技项目</div>
  115. <div class="row-content">
  116. <el-input placeholder="请输入搜索内容" size="medium" v-model="form.tech_search_keyword"
  117. @keyup.enter.native="searchTechList" class="input-with-select">
  118. <el-select v-model="form.tech_search_sel" style="width:142px" size="medium" slot="prepend">
  119. <el-option label="项目立项编号" value="0"></el-option>
  120. <el-option label="参与单位" value="1"></el-option>
  121. <el-option label="项目名称" value="2"></el-option>
  122. </el-select>
  123. <el-button size="medium" slot="append" icon="el-icon-search" @click="searchTechList"></el-button>
  124. </el-input>
  125. </div>
  126. </div>
  127. <div class="form-table">
  128. <div style="margin: 0 20px 30px 20px">
  129. <el-table ref="tableRef" border :data="tableData" style="width:100%" v-loading="loading" stripe>
  130. <el-table-column prop="no" label="项目立项编号" align="center" header-align="center"
  131. width="120"></el-table-column>
  132. <el-table-column prop="name" label="科技项目名称" align="center" header-align="center"
  133. width="200"></el-table-column>
  134. <el-table-column prop="institution" label="项目承担单位" align="center" header-align="center"
  135. width="200"></el-table-column>
  136. <el-table-column prop="all_institution" label="所有参与单位" align="center"
  137. header-align="center"></el-table-column>
  138. <el-table-column width="100" :label="$t('operation')" align="center" header-align="center">
  139. <template slot-scope="scope">
  140. <el-button type="primary" @click="selectedTechPrj(scope.row)">选择</el-button>
  141. </template>
  142. </el-table-column>
  143. </el-table>
  144. </div>
  145. </div>
  146. </div>
  147. </div>
  148. </div>
  149. </div>
  150. </div>
  151. </template>
  152. <script>
  153. import TopHeader from '../components/TopHeader.vue';
  154. import { getTechs, setOpenIApply, setNoOpenIApply, getCreateRepoUser, getCheckRepoName, getTopics } from '~/apis/modules/tech';
  155. export default {
  156. data() {
  157. return {
  158. form: {
  159. type: 'openi',
  160. url: '',
  161. url_err: false,
  162. repo_alias: '',
  163. alias_err: false,
  164. uid: '',
  165. repo_name: '',
  166. name_err: false,
  167. repo_url: '',
  168. topics: [],
  169. topicLoading: false,
  170. description: '',
  171. tech_search_sel: '0',
  172. tech_search_keyword: '',
  173. tech_obj: null,
  174. tech_show: '',
  175. tech_err: false,
  176. institution: [],
  177. institution_err: false,
  178. },
  179. selectTechPrj: false,
  180. loading: false,
  181. tableData: [],
  182. topicsList: [],
  183. institutionList: [],
  184. ownerList: [],
  185. ownerSelect: {},
  186. submitLoading: false,
  187. };
  188. },
  189. components: {
  190. TopHeader,
  191. },
  192. methods: {
  193. resetData() {
  194. this.form.url = '';
  195. this.form.url_err = false;
  196. this.form.repo_alias = '';
  197. this.form.alias_err = false;
  198. this.form.uid = this.ownerList.length ? this.ownerList[0].value : '';
  199. this.form.repo_name = '';
  200. this.form.repo_url = '';
  201. this.form.name_err = false;
  202. this.form.topics = [];
  203. this.form.topicLoading = false;
  204. this.form.description = '';
  205. this.form.tech_search_sel = '0';
  206. this.form.tech_search_keyword = '';
  207. this.form.tech_obj = null;
  208. this.form.tech_show = '';
  209. this.form.tech_err = false;
  210. this.form.institution = [];
  211. this.form.institution_err = false;
  212. this.tableData = [];
  213. this.topicsList = [];
  214. this.institutionList = [];
  215. this.submitLoading = false;
  216. },
  217. changeType() {
  218. this.resetData();
  219. },
  220. checkUrl() {
  221. this.form.url_err = !this.form.url;
  222. return !this.form.url_err;
  223. },
  224. checkRepoAlias() {
  225. const reg = /^[\u4E00-\u9FA5A-Za-z0-9_.-]{1,100}$/;
  226. const res = reg.test(this.form.repo_alias);
  227. this.form.alias_err = !res;
  228. return res;
  229. },
  230. checkRepoName() {
  231. const reg = /^[A-Za-z0-9_.-]{1,100}$/;
  232. const res = reg.test(this.form.repo_name);
  233. this.form.name_err = !res;
  234. return res;
  235. },
  236. checkTech() {
  237. this.form.tech_err = !this.form.tech_obj;
  238. return !this.form.tech_err;
  239. },
  240. checkinstitution() {
  241. this.form.institution_err = this.form.institution.length == 0;
  242. return !this.form.institution_err;
  243. },
  244. changeUrl() {
  245. if (this.form.type == 'openi') return;
  246. const owner = this.ownerSelect.Name;
  247. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  248. const repoValue = this.form.url.match(/^(.*\/)?((.+?)(\.git)?)$/)[3];
  249. this.form.repo_alias = repoValue;
  250. this.checkRepoAlias();
  251. getCheckRepoName({ owner: owner, q: repoValue }).then(res => {
  252. const repo_name = res.data.name;
  253. this.form.repo_name = repo_name;
  254. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  255. this.checkRepoName();
  256. }).catch(err => {
  257. console.log(err);
  258. this.form.repo_name = '';
  259. this.form.repo_url = '';
  260. });
  261. },
  262. changeAlias() {
  263. const owner = this.ownerSelect.Name;
  264. const aliasValue = this.form.repo_alias;
  265. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  266. if (aliasValue && this.checkRepoAlias()) {
  267. getCheckRepoName({ owner: owner, q: aliasValue }).then(res => {
  268. const repo_name = res.data.name;
  269. this.form.repo_name = repo_name;
  270. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  271. }).catch(err => {
  272. console.log(err);
  273. this.form.repo_name = '';
  274. this.form.repo_url = '';
  275. });
  276. } else {
  277. this.form.repo_name = '';
  278. this.form.repo_url = '';
  279. }
  280. },
  281. changeOwner(value) {
  282. this.ownerSelect = this.ownerList.filter(item => item.value == value)[0];
  283. this.form.repo_name && this.changeRepoName();
  284. },
  285. changeRepoName() {
  286. const owner = this.ownerSelect.Name;
  287. const repo_name = this.form.repo_name;
  288. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  289. if (this.checkRepoName()) {
  290. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  291. } else {
  292. this.form.repo_url = '';
  293. }
  294. },
  295. searchTopics(query) {
  296. if (query !== '') {
  297. this.form.topicLoading = true;
  298. getTopics({ q: query }).then(res => {
  299. this.form.topicLoading = false;
  300. const topics = res.data.topics || [];
  301. this.topicsList = topics.map(item => {
  302. return {
  303. value: item.topic_name,
  304. label: item.topic_name,
  305. }
  306. });
  307. }).catch(err => {
  308. this.topicsList = [];
  309. });
  310. } else {
  311. this.topicsList = [];
  312. }
  313. },
  314. goSelectTechPrj() {
  315. this.form.tech_search_sel = '0';
  316. this.form.tech_search_keyword = '';
  317. this.tableData = [];
  318. this.selectTechPrj = true;
  319. this.searchTechList();
  320. },
  321. clearTechPrj() {
  322. this.form.institution = [];
  323. this.institutionList = [];
  324. this.form.tech_show = '';
  325. this.form.tech_obj = null;
  326. this.form.tech_err = false;
  327. },
  328. goBack() {
  329. this.selectTechPrj = false;
  330. },
  331. searchTechList() {
  332. this.loading = true;
  333. getTechs({
  334. no: this.form.tech_search_sel == '0' ? this.form.tech_search_keyword : '',
  335. institution: this.form.tech_search_sel == '1' ? this.form.tech_search_keyword : '',
  336. name: this.form.tech_search_sel == '2' ? this.form.tech_search_keyword : '',
  337. }).then(res => {
  338. this.loading = false;
  339. res = res.data;
  340. this.tableData = res.data || [];
  341. }).catch(err => {
  342. console.log(err);
  343. this.loading = false;
  344. this.tableData = [];
  345. });
  346. },
  347. selectedTechPrj(item) {
  348. this.form.institution = [];
  349. this.institutionList = item.all_institution.split(',').map((item, index) => {
  350. return {
  351. key: item,
  352. value: item,
  353. }
  354. });
  355. this.form.tech_show = `【${item.no}】 ${item.name}`;
  356. this.form.tech_obj = item;
  357. this.checkTech();
  358. this.goBack();
  359. },
  360. submit() {
  361. const subData = {};
  362. let setApi = null;
  363. if (this.form.type == 'openi') {
  364. setApi = setOpenIApply;
  365. const r1 = this.checkUrl();
  366. const r2 = this.checkTech();
  367. const r3 = this.checkinstitution();
  368. if (r1 && r2 && r3) {
  369. subData.url = this.form.url;
  370. subData.no = this.form.tech_obj.no;
  371. subData.institution = this.form.institution.join(',');
  372. } else {
  373. return;
  374. }
  375. } else {
  376. setApi = setNoOpenIApply;
  377. const r1 = this.checkUrl();
  378. const r2 = this.checkTech();
  379. const r3 = this.checkinstitution();
  380. const r4 = this.checkRepoAlias();
  381. const r5 = this.checkRepoName();
  382. if (r1 && r2 && r3 && r4 && r5) {
  383. subData.url = this.form.url;
  384. subData.uid = this.form.uid;
  385. subData.repo_alias = this.form.repo_alias;
  386. subData.repo_name = this.form.repo_name;
  387. subData.topics = [...this.form.topics];
  388. subData.description = this.form.description;
  389. subData.no = this.form.tech_obj.no;
  390. subData.institution = this.form.institution.join(',');
  391. } else {
  392. return;
  393. }
  394. }
  395. // console.log(subData);
  396. // return;
  397. this.submitLoading = true;
  398. setApi(subData).then(res => {
  399. if (res.data && res.data.code == 0) {
  400. this.$message({
  401. type: 'success',
  402. message: this.$t('submittedSuccessfully'),
  403. });
  404. setTimeout(() => {
  405. window.location.href = '/tech/tech_view';
  406. }, 2000);
  407. } else {
  408. this.submitLoading = false;
  409. this.$message({
  410. type: 'info',
  411. message: res.data.msg
  412. });
  413. }
  414. }).catch(err => {
  415. this.submitLoading = false;
  416. this.$message({
  417. type: 'error',
  418. message: this.$t('submittedFailed'),
  419. });
  420. });
  421. },
  422. cancel() {
  423. window.history.back();
  424. }
  425. },
  426. beforeMount() {
  427. getCreateRepoUser().then(res => {
  428. const data = res.data.Data || [];
  429. this.ownerList = data.map(item => {
  430. return {
  431. value: item.ID,
  432. label: item.FullName || item.Name,
  433. ...item,
  434. }
  435. });
  436. this.ownerSelect = this.ownerList.length ? this.ownerList[0] : {};
  437. this.form.uid = this.ownerList.length ? this.ownerList[0].value : '';
  438. }).catch(err => {
  439. console.log(err);
  440. });
  441. },
  442. mounted() { },
  443. beforeDestroy() { },
  444. };
  445. </script>
  446. <style scoped lang="less">
  447. .title {
  448. text-align: center;
  449. margin: 30px 0;
  450. font-size: 14px;
  451. color: rgb(16, 16, 16);
  452. }
  453. .form-c,
  454. .form-select-tech-prj {
  455. display: flex;
  456. justify-content: center;
  457. .form-wrap {
  458. width: 1000px;
  459. background: rgb(255, 255, 255);
  460. border-color: rgb(212, 212, 213);
  461. border-width: 1px;
  462. border-style: solid;
  463. border-radius: 5px;
  464. box-sizing: border-box;
  465. .form-header {
  466. height: 45px;
  467. background: rgb(240, 240, 240);
  468. border-bottom: 1px solid rgb(212, 212, 213);
  469. color: rgb(16, 16, 16);
  470. padding-left: 16px;
  471. padding-right: 16px;
  472. font-size: 16px;
  473. font-weight: 700;
  474. display: flex;
  475. align-items: center;
  476. justify-content: space-between;
  477. }
  478. .form-content {
  479. margin-top: 40px;
  480. .form-row {
  481. display: flex;
  482. align-items: center;
  483. width: 80%;
  484. margin: 20px auto;
  485. padding-right: 50px;
  486. .row-label {
  487. flex: 2;
  488. text-align: right;
  489. margin-right: 24px;
  490. position: relative;
  491. &.required {
  492. &::after {
  493. position: absolute;
  494. top: -2px;
  495. right: -10px;
  496. content: '*';
  497. color: #db2828;
  498. }
  499. }
  500. &.baseline {
  501. align-self: baseline;
  502. margin-top: 11px;
  503. }
  504. }
  505. .row-content {
  506. flex: 9;
  507. .tips {
  508. margin-top: 4px;
  509. font-size: 14px;
  510. color: rgb(136, 136, 136);
  511. }
  512. }
  513. .reop-url-c {
  514. display: flex;
  515. align-items: center;
  516. }
  517. .openi-repo-url {
  518. color: rgba(16, 16, 16, 1);
  519. }
  520. &.form-row-err {
  521. .row-label {
  522. color: #9f3a38;
  523. }
  524. /deep/ .can-err .el-input__inner {
  525. color: #9f3a38;
  526. background: #fff6f6;
  527. border-color: #e0b4b4;
  528. }
  529. }
  530. }
  531. .form-table {
  532. margin-top: 30px;
  533. margin-bottom: 30px;
  534. /deep/ .el-table__header {
  535. th {
  536. background: rgb(249, 249, 249);
  537. font-size: 12px;
  538. color: rgb(136, 136, 136);
  539. font-weight: normal;
  540. }
  541. }
  542. /deep/ .el-table__body {
  543. td {
  544. font-size: 12px;
  545. }
  546. }
  547. /deep/ .el-radio__label {
  548. display: none;
  549. }
  550. }
  551. .form-btn-group {
  552. display: flex;
  553. justify-content: center;
  554. align-items: center;
  555. margin: 30px 0 40px;
  556. }
  557. }
  558. }
  559. }
  560. .btn {
  561. color: rgb(2, 0, 4);
  562. background-color: rgb(194, 199, 204);
  563. border-color: rgb(194, 199, 204);
  564. &.confirm-btn {
  565. color: #fff;
  566. background-color: rgb(56, 158, 13);
  567. border-color: rgb(56, 158, 13);
  568. margin-right: 20px;
  569. }
  570. }
  571. .btn-select {
  572. background-color: rgb(50, 145, 248) !important;
  573. color: white !important;
  574. border-radius: 0 !important;
  575. height: 35px;
  576. }
  577. .owner-sel {
  578. /deep/ .el-input__inner {
  579. padding-left: 48px;
  580. }
  581. }
  582. .owner-item {
  583. display: flex;
  584. align-items: center;
  585. .owner-img {
  586. width: 24px;
  587. height: 24px;
  588. margin-right: 10px;
  589. }
  590. }
  591. </style>