CommonContent.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. import { DataModel, transformArrayDataModel, type NewDataModel } from '@imengyu/js-request-transform';
  2. import ApiCofig from '@/common/config/ApiCofig';
  3. import { AppServerRequestModule } from './RequestModules';
  4. import { transformSomeToArray } from './Utils';
  5. import { assertNotNull, RequestApiConfig, RequestOptions, type QueryParams } from '@imengyu/imengyu-utils';
  6. export class GetColumListParams extends DataModel<GetColumListParams> {
  7. public constructor() {
  8. super(GetColumListParams);
  9. this.setNameMapperCase('Camel', 'Snake');
  10. }
  11. setModelId(val: number) {
  12. this.modelId = val;
  13. return this;
  14. }
  15. setMainBodyColumnId(val: number) {
  16. this.mainBodyColumnId = val;
  17. return this;
  18. }
  19. setFlag(val: 'hot'|'recommend'|'top') {
  20. this.flag = val;
  21. return this;
  22. }
  23. setSize(val: number) {
  24. this.size = val;
  25. return this;
  26. }
  27. modelId?: number;
  28. /**
  29. * 主体栏目id
  30. */
  31. mainBodyColumnId: number = 0;
  32. /**
  33. * 标志:hot=热门,recommend=推荐,top=置顶
  34. */
  35. flag ?: 'hot'|'recommend'|'top';
  36. /**
  37. * 内容数量,默认4
  38. */
  39. size = 4;
  40. }
  41. export class GetContentListParams extends DataModel<GetContentListParams> {
  42. public constructor() {
  43. super(GetContentListParams);
  44. this.setNameMapperCase('Camel', 'Snake');
  45. this._convertTable = {
  46. ids: {
  47. customToServerFn: (val) => (val as number[]).join(','),
  48. customToClientFn: (val) => (val as string).split(',').map((item) => parseInt(item)),
  49. },
  50. }
  51. }
  52. setMainBodyColumnId(val: number|number[]|string|undefined) {
  53. this.mainBodyColumnId = val;
  54. return this;
  55. }
  56. setFlag(val: 'hot'|'recommend'|'top') {
  57. this.flag = val;
  58. return this;
  59. }
  60. setIds(val: number[]) {
  61. this.ids = val;
  62. return this;
  63. }
  64. setType(val: 1|2|3|4) {
  65. this.type = val;
  66. return this;
  67. }
  68. setSize(val: number) {
  69. this.size = val;
  70. return this;
  71. }
  72. setKeywords(val: string) {
  73. this.keywords = val;
  74. return this;
  75. }
  76. setModelId(val: number) {
  77. this.modelId = val;
  78. return this;
  79. }
  80. static TYPE_ARTICLE = 1;
  81. static TYPE_AUDIO = 2;
  82. static TYPE_VIDEO = 3;
  83. static TYPE_IMAGE = 4;
  84. modelId ?: number;
  85. /**
  86. * 主体栏目id
  87. */
  88. mainBodyColumnId?: number|number[]|string|undefined = 0;
  89. /**
  90. * 标志:hot=热门,recommend=推荐,top=置顶
  91. */
  92. flag ?: 'hot'|'recommend'|'top';
  93. /**
  94. * 内容id(逗号隔开)如:3 或者 1,2,3
  95. */
  96. ids?: number[];
  97. /**
  98. * 类型:1=文章,2=音频,3=视频,4=相册
  99. */
  100. type?: 1|2|3|4;
  101. /**
  102. * 内容数量,默认4
  103. */
  104. size = 4;
  105. /**
  106. * 关键字查询
  107. */
  108. keywords?: string;
  109. }
  110. export class GetColumContentList extends DataModel<GetColumContentList> {
  111. constructor() {
  112. super(GetColumContentList, "主体栏目列表");
  113. this.setNameMapperCase('Camel', 'Snake');
  114. this._convertTable = {
  115. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  116. name: { clientSide: 'string', serverSide: 'string', clientSideRequired: true },
  117. content_list: {
  118. clientSide: 'array',
  119. clientSideRequired: true,
  120. clientSideChildDataModel: GetContentListItem,
  121. },
  122. }
  123. }
  124. name = '';
  125. overview = '';
  126. }
  127. export class GetContentListItem extends DataModel<GetContentListItem> {
  128. constructor() {
  129. super(GetContentListItem, "内容列表");
  130. this.setNameMapperCase('Camel', 'Snake');
  131. this._convertTable = {
  132. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  133. mainBodyColumnId: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  134. title: { clientSide: 'string', serverSide: 'string', clientSideRequired: true },
  135. isGuest: { clientSide: 'boolean', serverSide: 'number' },
  136. isLogin: { clientSide: 'boolean', serverSide: 'number' },
  137. isComment: { clientSide: 'boolean', serverSide: 'number' },
  138. isLike: { clientSide: 'boolean', serverSide: 'number' },
  139. isCollect: { clientSide: 'boolean', serverSide: 'number' },
  140. latitude: { clientSide: 'number', serverSide: 'number' },
  141. longitude: { clientSide: 'number', serverSide: 'number' },
  142. publishAt: { clientSide: 'date', serverSide: 'string' },
  143. flag: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  144. tags: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  145. keywords: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  146. type: { clientSide: 'number', serverSide: 'number' },
  147. };
  148. this._convertKeyType = (key, direction) => {
  149. if (key.endsWith('Time'))
  150. return {
  151. clientSide: 'date',
  152. serverSide: 'string',
  153. };
  154. return undefined;
  155. };
  156. }
  157. id = 0;
  158. mainBodyColumnId = 0;
  159. latitude = 0;
  160. longitude = 0;
  161. mapX = '';
  162. mapY = '';
  163. from = '';
  164. modelId = 0;
  165. title = '!title';
  166. region = 0;
  167. image = '';
  168. thumbnail = '';
  169. desc = '!desc';
  170. content = '!content';
  171. type = 0;
  172. keywords ?: string[];
  173. flag ?: string[];
  174. tags ?: string[];
  175. views = 0;
  176. comments = 0;
  177. likes = 0;
  178. collects = 0;
  179. dislikes = 0;
  180. district = '';
  181. publishAt = new Date();
  182. }
  183. export class GetContentDetailItem extends DataModel<GetContentDetailItem> {
  184. constructor() {
  185. super(GetContentDetailItem, "内容详情");
  186. this.setNameMapperCase('Camel', 'Snake');
  187. this._convertTable = {
  188. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  189. title: { clientSide: 'string', serverSide: 'string', clientSideRequired: true },
  190. isGuest: { clientSide: 'boolean', serverSide: 'number' },
  191. isLogin: { clientSide: 'boolean', serverSide: 'number' },
  192. isComment: { clientSide: 'boolean', serverSide: 'number' },
  193. isLike: { clientSide: 'boolean', serverSide: 'number' },
  194. isCollect: { clientSide: 'boolean', serverSide: 'number' },
  195. publishAt: { clientSide: 'date', serverSide: 'string' },
  196. flag: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  197. tags: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  198. type: { clientSide: 'number', serverSide: 'number' },
  199. }
  200. this._convertKeyType = (key, direction) => {
  201. if (key.endsWith('Time'))
  202. return {
  203. clientSide: 'date',
  204. serverSide: 'string',
  205. };
  206. else if (key.endsWith('List')) {
  207. return [
  208. { clientSide: 'map', serverSide: 'original'},
  209. { clientSide: 'array', clientSideChildDataModel: GetContentDetailItem, serverSide: 'original' },
  210. ]
  211. }
  212. return undefined;
  213. };
  214. this._afterSolveServer = () => {
  215. if (!this.image && this.images && this.images && this.images.length > 0 ) {
  216. this.image = this.images[0]
  217. }
  218. if ((!this.images || this.images.length == 0) && this.image) {
  219. this.images = [ this.image ]
  220. }
  221. }
  222. }
  223. id = 0;
  224. from = '';
  225. modelId = 0;
  226. type = 0;
  227. title = '';
  228. region = 0;
  229. image = '';
  230. images = [] as string[];
  231. audio = '';
  232. video = '';
  233. desc = '';
  234. flag ?: string[];
  235. tags ?: string[];
  236. views = 0;
  237. comments = 0;
  238. likes = 0;
  239. collects = 0;
  240. dislikes = 0;
  241. isLogin = false;
  242. isGuest = false;
  243. isComment = false;
  244. isLike = false;
  245. isCollect = false;
  246. content = '';
  247. publishAt = new Date();
  248. associationMeList = [] as {
  249. id: number,
  250. title: string,
  251. image: string,
  252. thumbnail: string,
  253. }[];
  254. }
  255. export class CategoryListItem extends DataModel<CategoryListItem> {
  256. constructor() {
  257. super(CategoryListItem, "分类列表");
  258. this.setNameMapperCase('Camel', 'Snake');
  259. this._convertTable = {
  260. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  261. pid: { clientSide: 'number', serverSide: 'number' },
  262. haschild: { clientSide: 'boolean', serverSide: 'number' },
  263. }
  264. }
  265. id !: number;
  266. pid !: number;
  267. title = '';
  268. status = 'normal';
  269. weight = 0;
  270. spacer = '';
  271. haschild = false;
  272. children?: CategoryListItem[];
  273. }
  274. /**
  275. * 说明:
  276. * * 主通用内容接口。
  277. */
  278. export class CommonContentApi extends AppServerRequestModule<DataModel> {
  279. constructor(
  280. mainBodyId = ApiCofig.mainBodyId,
  281. modelId = 0, debugName = 'CommonContent',
  282. mainBodyColumnId?: number|number[]) {
  283. super();
  284. this.modelId = modelId;
  285. this.mainBodyId = mainBodyId;
  286. this.mainBodyColumnId = mainBodyColumnId;
  287. this.debugName = debugName;
  288. }
  289. public mainBodyId: number;
  290. public mainBodyColumnId?: number|number[];
  291. public modelId: number;
  292. protected debugName: string;
  293. private toStringArray(arr: number|number[]|string|undefined) {
  294. if (typeof arr === 'undefined')
  295. return '';
  296. return typeof arr === 'object' ? arr.join(',') : arr.toString();
  297. }
  298. /**
  299. * 获取分类列表
  300. * @param type 根级类型:1=区域、2=级别、3=文物类型、4=非遗类型、42=事件类型
  301. * @param withself 是否返回包含自己:true=是,false=否 ,默认false
  302. * @returns
  303. */
  304. async getCategoryList(
  305. type?: number,
  306. withself?: boolean,
  307. ) {
  308. const res = await this.get<any[]>('/content/category/getCategoryList', '获取分类列表', {
  309. type,
  310. is_tree: false,
  311. withself,
  312. });
  313. return transformArrayDataModel<CategoryListItem>(CategoryListItem, res.requireData(), `获取分类列表`, true);
  314. }
  315. /**
  316. * 用于获取某一个分类需要用的子级
  317. * @param pid 父级
  318. * @returns
  319. */
  320. async getCategoryChildList(pid?: number) {
  321. const res = await this.get('/content/category/getCategoryOnlyChildList', '获取分类子级列表', {
  322. pid,
  323. });
  324. return transformArrayDataModel<CategoryListItem>(
  325. CategoryListItem,
  326. transformSomeToArray(res.data),
  327. `获取分类列表`,
  328. true
  329. );
  330. }
  331. /**
  332. * 地址下拉带搜索
  333. * @param pid 父级
  334. * @returns
  335. */
  336. async searchRegion(keywords?: string) {
  337. const res = await this.get('/content/category/searchRegion', '地址搜索', {
  338. keywords,
  339. });
  340. return transformArrayDataModel<CategoryListItem>(
  341. CategoryListItem,
  342. transformSomeToArray(res.data),
  343. `地址下拉带搜索`,
  344. true
  345. );
  346. }
  347. /**
  348. * 主体栏目列表
  349. * @param params 参数
  350. * @param querys 额外参数
  351. * @returns
  352. */
  353. async getColumList<T extends DataModel = GetColumContentList>(params: GetColumListParams, modelClassCreator: NewDataModel = GetColumContentList, querys?: QueryParams) {
  354. const res = await this.get<{
  355. list: T[],
  356. total: number,
  357. }>('/content/content/getMainBodyColumnContentList', `${this.debugName} 主体栏目列表`, {
  358. main_body_id: this.mainBodyId,
  359. model_id: this.modelId,
  360. ...params.toServerSide(),
  361. ...querys,
  362. });
  363. assertNotNull(res.data, '获取主体栏目列表失败');
  364. return {
  365. list: transformArrayDataModel<T>(modelClassCreator, res.data.list, `${this.debugName} 主体栏目列表`, true),
  366. total: res.data.total as number,
  367. };
  368. }
  369. /**
  370. * 模型内容列表
  371. * @param params 参数
  372. * @param page 页码
  373. * @param pageSize 页大小
  374. * @param querys 额外参数
  375. * @returns
  376. */
  377. async getContentList<T extends DataModel = GetContentListItem>(
  378. params: GetContentListParams,
  379. page: number,
  380. pageSize: number = 10,
  381. modelClassCreator: NewDataModel = GetContentListItem,
  382. isNearby?: boolean,
  383. querys?: QueryParams
  384. ) {
  385. const res = await this.get<{
  386. list: any[],
  387. total: number,
  388. }>(`/content/content/${isNearby ? 'getNearbyList' : 'getContentList'}`, `${this.debugName} 模型内容列表`, {
  389. ...params.toServerSide(),
  390. model_id: params.modelId || this.modelId,
  391. main_body_id: params.mainBodyId || this.mainBodyId,
  392. main_body_column_id: this.toStringArray(params.mainBodyColumnId || this.mainBodyColumnId),
  393. page,
  394. pageSize,
  395. ...querys,
  396. });
  397. let resList : any = null;
  398. let resTotal : any = null;
  399. if (res.data?.list && Array.isArray(res.data.list)) {
  400. resList = res.data.list;
  401. resTotal = res.data.total ?? resList.length;
  402. }
  403. else if (res.data && Array.isArray(res.data)) {
  404. resList = res.data;
  405. resTotal = resList.length;
  406. } else
  407. resList = res.data;
  408. if (resList === null)
  409. return { list: [], total: 0 };
  410. return {
  411. list: transformArrayDataModel<T>(modelClassCreator, resList, `${this.debugName} 模型内容列表`, true),
  412. total: resTotal as number,
  413. };
  414. }
  415. /**
  416. * 内容详情
  417. * @param id id
  418. * @param querys 额外参数
  419. * @returns
  420. */
  421. async getContentDetail<T extends DataModel = GetContentDetailItem>(id: number, modelClassCreator: NewDataModel = GetContentDetailItem, modelId?: number, querys?: QueryParams) {
  422. return (await this.get('/content/content/getContentDetail', `${this.debugName} (${id}) 内容详情`, {
  423. main_body_id: this.mainBodyId,
  424. model_id: modelId ?? this.modelId,
  425. id,
  426. ...querys,
  427. }, modelClassCreator)).data as T;
  428. }
  429. /**
  430. * 上传文件到服务器
  431. */
  432. async uploadFile(file: string, fileType?: "image" | "video" | "audio" | undefined, name = 'file', data?: any, receiveTask?: (result: UniApp.UploadTask) => void) {
  433. let url = RequestApiConfig.getConfig().BaseUrl + '/common/upload';
  434. let req : RequestOptions = {
  435. method: 'POST',
  436. data: data,
  437. headers: {},
  438. }
  439. if (this.config.requestInterceptor) {
  440. const { newReq, newUrl } = await this.config.requestInterceptor(url, req);
  441. url = newUrl;
  442. data = newReq;
  443. }
  444. return new Promise<{
  445. fullurl: string,
  446. url: string
  447. }>((resolve, reject) => {
  448. const task = uni.uploadFile({
  449. url: url,
  450. name,
  451. header: req.headers,
  452. filePath: file,
  453. formData: data,
  454. fileType,
  455. success: (result) => {
  456. let data = JSON.parse(result.data);
  457. if (data.code !== 1)
  458. throw new Error(data.msg ?? 'code: ' + data.code);
  459. resolve(data.data);
  460. },
  461. fail(result) {
  462. reject(result);
  463. },
  464. })
  465. if (receiveTask)
  466. receiveTask(task);
  467. })
  468. }
  469. /**
  470. * 上传图片
  471. * @param tempFilePath 临时文件路径
  472. * @returns
  473. */
  474. async uploadImages(tempFilePath: string) {
  475. const res = await this.uploadFile(tempFilePath, 'image');
  476. return res.fullurl;
  477. }
  478. }
  479. export default new CommonContentApi(undefined, 0, '默认通用内容');