EDRI OA

OA小助手

目前为 2024-12-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name EDRI OA
  3. // @namespace bsn
  4. // @version 1.1.5
  5. // @description OA小助手
  6. // @author 不死鸟
  7. // @require https://unpkg.com/vue@3.5.13/dist/vue.global.prod.js
  8. // @require data:application/javascript,unsafeWindow.Vue%3DVue%2Cthis.Vue%3DVue%3B
  9. // @require https://unpkg.com/naive-ui@2.40.3/dist/index.js
  10. // @require https://unpkg.com/lscache@1.3.2/lscache.js
  11. // @require https://unpkg.com/moment@2.30.1/moment.js
  12. // @require https://unpkg.com/moment@2.30.1/locale/zh-cn.js
  13. // @require https://unpkg.com/html2canvas@1.4.1/dist/html2canvas.js
  14. // @require https://update.greasyfork.org/scripts/520145/1500083/bsn-libs.js
  15. // @match https://oa.edri.cn/Portal/Main/Index*
  16. // @grant unsafeWindow
  17. // @grant GM_addStyle
  18. // @license GPL version 3
  19. // ==/UserScript==
  20.  
  21. (function() {
  22. GM_addStyle(``);
  23.  
  24. const COM = {
  25. template: `
  26. <n-form label-placement="left" label-width="auto" size="small" :show-feedback="false">
  27. <n-form-item>
  28. <n-button type="info" style="width: 100%" @click="fillContractInvoice">填写合同发票</n-button>
  29. </n-form-item>
  30. </n-form>
  31. `,
  32. props: {},
  33. emits: ['closeDrawer'],
  34. setup(props, { emit }) {
  35. const message = naive.useMessage();
  36. const dialog = window.createNaiveDialog();
  37. const testing = false;
  38. const data = Vue.reactive({
  39. contractInvoice: '',
  40. async fillContractInvoice() {
  41. data.contractInvoice = testing
  42. ? `发票开票申请资料填写
  43. 序号 类别 内容 备注
  44. 一、基本信息
  45. 1 合同名称\t苏州xx公司疫苗项目\t
  46. 2 合同编号\t2025-xx-xxx\t
  47. 3 发票内容\t设计费\t
  48. 4 发票金额\t10000\t
  49. 5 发票类型\t专用发票\t
  50. 6 预计收款日期\t2025/1/1\t
  51. 7 项目负责人\t李洪群\t
  52. 二、我公司开票资料(如果选择基本户,不用填写开户行及帐号)
  53. 1 是否为基本户\t\t
  54. 2 开户行\t\t
  55. 3 账号\t\t
  56. 三、客户开票资料
  57. 1 客户开票名称\t苏州xx公司\t
  58. 2 税号\t123456789012345678\t
  59. 3 单位地址\t江苏苏州工业园区\t
  60. 4 电话号码\t0512-12345678\t
  61. 5 开户银行\t中国银行\t
  62. 6 银行账户\t123456789012\t
  63. 四、其他信息
  64. 1 备注\t\t
  65. `
  66. : '';
  67. let isOk = await dialog.successAsync({
  68. title: '合同发票',
  69. showIcon: false,
  70. content: () =>
  71. Vue.h(naive.NInputGroup, {}, () => [
  72. Vue.h(naive.NInput, {
  73. value: data.contractInvoice,
  74. type: 'textarea',
  75. placeholder: '请输入合同发票信息',
  76. rows: 15,
  77. ['onUpdate:value']: v => {
  78. data.contractInvoice = v;
  79. }
  80. }),
  81. Vue.h(
  82. naive.NButton,
  83. {
  84. type: 'info',
  85. onclick: async () => (data.contractInvoice = await window.getClipboardText())
  86. },
  87. () => '粘贴板'
  88. )
  89. ]),
  90. positiveText: '下一步',
  91. negativeText: '取消'
  92. });
  93. if (!isOk) return;
  94. const infos = {
  95. basic: {
  96. contractName: { titles: ['合同名称'], value: '', remark: '' },
  97. contractNo: { titles: ['合同编号'], value: '', remark: '' },
  98. invoiceContent: { titles: ['发票内容'], value: '', remark: '' },
  99. invoiceAmount: { titles: ['发票金额'], value: '', remark: '' },
  100. invoiceType: { titles: ['发票类型'], value: '', remark: '' },
  101. estimatedCollectionDate: { titles: ['预计收款日期'], value: '', remark: '' },
  102. projectLeader: { titles: ['项目负责人'], value: '', remark: '' }
  103. },
  104. company: {
  105. baseAccounts: { titles: ['是否为基本户'], value: '', remark: '' },
  106. bankOfDeposit: { titles: ['开户行'], value: '', remark: '' },
  107. accounts: { titles: ['账号'], value: '', remark: '' }
  108. },
  109. customer: {
  110. customerName: { titles: ['客户开票名称'], value: '', remark: '' },
  111. dutyParagraph: { titles: ['税号'], value: '', remark: '' },
  112. customerAddress: { titles: ['单位地址'], value: '', remark: '' },
  113. telephone: { titles: ['电话号码'], value: '', remark: '' },
  114. customerbank: { titles: ['开户银行'], value: '', remark: '' },
  115. bankAccounts: { titles: ['银行账户'], value: '', remark: '' }
  116. },
  117. other: {
  118. remarks: { titles: ['备注'], value: '', remark: '' }
  119. }
  120. };
  121. const patterns = [];
  122. Object.keys(infos).forEach(key => {
  123. const o = infos[key];
  124. Object.keys(o).forEach(key2 => {
  125. const o2 = o[key2];
  126. o2.titles.forEach(title => {
  127. const matches = new RegExp(`(${title}\\s{0,})\\t(.{0,})\\t(.{0,})`).exec(
  128. data.contractInvoice
  129. );
  130. if (matches) {
  131. const [_, head, content, remark] = matches;
  132. const v = content.trim();
  133. const r = remark.trim();
  134. o2.value = v;
  135. o2.remark = r;
  136. patterns.push(head + '\t');
  137. if (v) patterns.push(v);
  138. if (r) patterns.push('\t' + remark);
  139. }
  140. });
  141. });
  142. });
  143. await sleep(300);
  144. isOk = await dialog.successAsync({
  145. title: '匹配结果',
  146. showIcon: false,
  147. content: () =>
  148. Vue.h(naive.NScrollbar, { style: 'max-height: calc(100vh - 200px)' }, () =>
  149. Vue.h(naive.NHighlight, {
  150. text: data.contractInvoice,
  151. patterns: patterns,
  152. style: 'word-break: normal;white-space: pre-wrap'
  153. })
  154. ),
  155. positiveText: '下一步',
  156. negativeText: '取消'
  157. });
  158. if (!isOk) return;
  159. emit('closeDrawer');
  160. const iframe = window.findAll({ selectors: 'iframe' }).find(el => {
  161. const srcAttr = Array.from(el.attributes).find(x => x.name === 'src');
  162. return srcAttr && srcAttr.nodeValue.startsWith('/MvcConfig/UI/Form');
  163. });
  164. window.simulateOperate([
  165. {
  166. type: 'input',
  167. parent: iframe,
  168. selectors: '[name="ContractName"]',
  169. value: infos.basic.contractName.value
  170. },
  171. {
  172. type: 'input',
  173. parent: iframe,
  174. selectors: '[name="ContractNo"]',
  175. value: infos.basic.contractNo.value
  176. },
  177. {
  178. type: 'input',
  179. parent: iframe,
  180. selectors: '[name="InvoiceContent"]',
  181. value: infos.basic.invoiceContent.value
  182. },
  183. {
  184. type: 'input',
  185. parent: iframe,
  186. selectors: '[name="InvoiceAmount"]',
  187. value: infos.basic.invoiceAmount.value,
  188. findTarget: el => {
  189. const parentEl = el.parentElement;
  190. return parentEl.getElementsByClassName('mini-buttonedit-input')[0];
  191. }
  192. },
  193. {
  194. type: 'click',
  195. parent: iframe,
  196. selectors: '[name="InvoiceType"]',
  197. focusable: true,
  198. waiting: 500,
  199. findTarget: el => {
  200. return el.parentElement.querySelector('.mini-buttonedit-input');
  201. }
  202. },
  203. {
  204. type: 'click',
  205. parent: iframe,
  206. selectors: '.mini-popup table tr',
  207. focusable: true,
  208. waiting: 1000,
  209. findTarget: el => {
  210. return el.innerText.includes(infos.basic.invoiceType.value) &&
  211. el.innerText.includes('电子')
  212. ? el
  213. : undefined;
  214. }
  215. },
  216. {
  217. type: 'input',
  218. parent: iframe,
  219. selectors: '[name="EstimatedCollectionDate"]',
  220. value: infos.basic.estimatedCollectionDate.value,
  221. waiting: 500,
  222. findTarget: el => {
  223. const parentEl = el.parentElement;
  224. return parentEl.getElementsByClassName('mini-buttonedit-input')[0];
  225. }
  226. },
  227. {
  228. type: 'input',
  229. parent: iframe,
  230. selectors: '[name="ProjectLeaderName"]',
  231. value: infos.basic.projectLeader.value,
  232. focusable: true,
  233. waiting: 500
  234. },
  235. {
  236. type: 'click',
  237. parent: iframe,
  238. selectors: '[name="BaseAccounts"]',
  239. findTarget: el => {
  240. const parentEl = el.parentElement;
  241. return Array.from(parentEl.querySelectorAll('label')).find(
  242. x => x.innerText === infos.company.baseAccounts.value
  243. );
  244. }
  245. },
  246. {
  247. type: 'input',
  248. parent: iframe,
  249. selectors: '[name="BankOfDeposit"]',
  250. value: infos.company.bankOfDeposit.value
  251. },
  252. {
  253. type: 'input',
  254. parent: iframe,
  255. selectors: '[name="Accounts"]',
  256. value: infos.company.accounts.value
  257. },
  258. {
  259. type: 'input',
  260. parent: iframe,
  261. selectors: '[name="CustomerName"]',
  262. value: infos.customer.customerName.value
  263. },
  264. {
  265. type: 'input',
  266. parent: iframe,
  267. selectors: '[name="Dutyparagraph"]',
  268. value: infos.customer.dutyParagraph.value
  269. },
  270. {
  271. type: 'input',
  272. parent: iframe,
  273. selectors: '[name="CustomerAddress"]',
  274. value: infos.customer.customerAddress.value
  275. },
  276. {
  277. type: 'input',
  278. parent: iframe,
  279. selectors: '[name="Telephone"]',
  280. value: infos.customer.telephone.value
  281. },
  282. {
  283. type: 'input',
  284. parent: iframe,
  285. selectors: '[name="Customerbank"]',
  286. value: infos.customer.customerbank.value
  287. },
  288. {
  289. type: 'input',
  290. parent: iframe,
  291. selectors: '[name="BankAccounts"]',
  292. value: infos.customer.bankAccounts.value
  293. },
  294. {
  295. type: 'input',
  296. parent: iframe,
  297. selectors: '[name="Remarks"]',
  298. value: infos.other.remarks.value
  299. }
  300. ]);
  301. }
  302. });
  303. return data;
  304. }
  305. };
  306. window.initVue3(COM);
  307. })();