Outlook手册

发送邮件后撤回或替换邮件

邮件作为工作中必要的沟通工具,每次在发送邮件时都需要谨慎。但是不可避免的,我们有时候还是会发送收件人,或者是邮件主体中有错误。
这时候,我们可以借助outlook的撤回或者是替换邮件功能。

  • 只适用于发件人和收件人都是Outlook
  • 只适用于收件人未查看邮件,及只能撤回或者替换收件人未读的邮件

撤回和替换邮件

  • 选择”已发送项目”文件夹
  • 双击打开需要撤回或者是替换的邮件(注意一定要双击打开)
  • 在打开的窗口中->邮件->移动->撤回邮件、重新发送邮件
  • 单击“删除该邮件的未读副本”或“删除未读副本并用新邮件替换”,然后单击“确定”

recall-or-resend-message.png

根据邮件创建规则

  • 右键单击现有消息,然后选择 规则 > 创建规则。
  • 选择一个条件,然后根据该条件选择要对该邮件执行的操作。
  • 例如,要将含有某个标题的邮件移动到特定文件夹,请选择“主题包含”条件,选择“将该项目移动到文件夹”,选择一个文件夹或单击“新建”创建一个文件夹,然后选择“确定”。
  • 创建规则完成后,选择“确定”。
  • 要立即使用该规则,选中“对当前文件夹中的现有邮件立即运行此规则”复选框,然后选择“确定”。
  • 现在邮件将出现在该文件夹中。

RPA教程

RPA简介

桌面流是Power Automate中用于实现桌面自动化操作,全称是机器人流程自动化,简称RPA

安装

安装Power Automate桌面版

  • 下载Power Automate安装程序
  • 在下载目录下双击打开Setup.Microsoft.PowerAutomate.exe选中需要的配置进行安装
  • 安装成功后,安装浏览器组件,两者选一
    • Google Chrome
    • Microsoft Edge
  • 点击Launch App即可启动
  • 输入Power Automate对应的Microsoft Account

示例

提取中行外汇数据

  • 打开桌面版Power Automate
  • 新建一个Flow,命名为中行外汇数据
  • 选择Browser automation
  • 双击添加Launch new Microsoft Edge
  • 设置如下属性:
1
2
3
4
Launch mode: Launch new Instance
Initial URL: https://srh.bankofchina.com/search/whpj/search_cn.jsp
Windows state: Normal
Variales produced: BankOfChina
  • 双击添加Set drop-down list value on web page
  • 设置如下属性
1
Web browser instance: %BankOfChina%
  • 点击UI element下拉框
  • 点击Add ui element
  • 浏览器打开https://srh.bankofchina.com/search/whpj/search_cn.jsp
  • 鼠标移动至选择货币下拉框
  • 按住Ctrl+点击鼠标左键
  • 设置Operation下拉框为Select options by name
  • 设置Option name为卢布
  • 双击添加Press button on web page
  • 设置Web browser instance%BankOfChina%
  • 点击UI element下拉
  • 点击Add ui element
  • 鼠标移动至搜索按钮,按住Ctrl+点击鼠标左键
  • 双击添加Extract data from web page
  • 设置Web browser instance%BankOfChina%
  • 设置Store data modeVariable(如果希望把数据保存到Excel文件,可以选择Excel spreadsheet)
  • 切换到Edge浏览器,会自动打开Live web helper
  • 移动鼠标选中汇率所在表格
  • 右键点击Extract entire HTML table
  • 移动鼠标至分页最右侧按钮
  • 右键点击set element as paper
  • 点击finish
  • 按需设置Max web pages to process,此处示例设置为2
  • 点击save
  • 点击运行查看结果

中行外汇数据

RPA网页爬虫之A股上市公司列表

在上期的教程中,我们实现了提取整个HTML Table的内容,不过一般情况下,我们只需要部分数据,今天我们实现提取同花顺网页中所有A股上市公司的代码、公司名和详细信息的链接。

  • 新建一个RPA流
  • 双击Launch new Microsoft Edge以添加启动Edge
  • 设置如下属性为
1
2
3
4
5
6
Launch mode:
Launch new Instance
Initial URL:
http://q.10jqka.com.cn/#refCountId=www_50a1b74a_693
Variables produced:
Invested
  • 双击Extract data from web page以实现从网页提取指定数据
  • 设置如下属性为
1
2
3
4
5
6
7
8
Web browser instance:
%Invested%
Extract data from:
All available
Store data mode:
Excel spreadsheet
Variables produced:
InvestedList
  • 初始设置结束后,我们接下来要配置读取指定信息
  • 打开http://q.10jqka.com.cn/#refCountId=www_50a1b74a_693网页,等待Live web helper自动打开
  • 由于我们只需要提取指定数据,所以我们通过extract element value
  • 我们以代码名称链接为例
  • 鼠标移动至序号为1的代码,鼠标点击右键并点击Extract element value,选择Text:('301112')
  • 鼠标移动至序员为1的名称,鼠标点击右键并点击Extract element value,选择Text:('N信邦')
  • 鼠标移动至序号为1的名称, 鼠标点击右键并点击Extract element value, 选择Href: (http://stockpage...`)`
  • 鼠标移动至序员为2的代码, 鼠标点击右键并点击Extract element value, 选择 Text: ('688297')
  • 滚动页面, 移动鼠标至下一页
  • 点击鼠标右键, 选择Set element as pager
  • 这时候Live web helper会自动实现解析当前页面的结构
    提取A股列表LiveWebHelper
  • 点击Finish->点击Save
  • 点击运行查看效果

RPA网页爬虫之A股上市公司列表_结果

BingMaps教程

创建Bing Maps Key

在使用Bing Maps Apis之前,我们必须要有Bing Maps Key

  • 前往Bing Maps Dev Center
    • 可以通过Microsoft Account登陆,然后创建Bing Maps Account
  • 点击My account
  • 选中My Keys
  • 点击Click here to create a new key.
  • 填入必要的信息
    • Application name 程序名称
    • Key type Key的类型
    • Application type 程序的类型
  • 点击Create
  • 在生成的界面复制Key

参数说明

  • BingMapsKey Bing Maps Keys(密钥)
  • point 地球上由纬度和经度指定的点
    • 坐标是用逗号分隔的值
    • 顺序指定: 纬度、经度
    • 示例:double[-90, +90][-180,+180]47.610679194331169,-122.10788659751415

根据经纬度查位置信息

1
http://dev.virtualearth.net/REST/v1/Locations/47.64054,-122.12934?o=xml&key={BingMapsKey}  

获取静态地图

1
http://dev.virtualearth.net/REST/v1/Imagery/Map/Road/47.619048,-122.35384/15?mapSize=500,500&pp=47.620495,-122.34931;21;AA&pp=47.619385,-122.351485;;AB&pp=47.616295,-122.3556;22&mapMetadata=1&o=xml&key={BingMapsKey}

SharePoint教程

SharePoint集合DisplayName与InternalName

在SharePoint中为集合创建列的时候,SharePoint会创建两个名字

  • 外部名称,主要用于显示ViewForms,可以任意修改
  • 内部名称,作为列的唯一标识,不可修改
    一般来说,在创建字段的时候,建议命名先以英文命名(不包含任何特殊字符),保存成功后,将修改外部名称。
    如果已经登陆,可以通过URL直接访问
    1
    https://edwarddev365.sharepoint.com/sites/meekou/_api/web/lists/getbytitle('员工信息登记')/fields?$select=StaticName,Title&$filter=Title eq '得分'

查找列对应的内部名称

  • 打开SharePoint List
  • 右上角设置
  • 点击列表设置
  • 部分选择想要查看的列,如得分,点击
    列名
  • 浏览器地址栏最后的Field=_x5f97__x5206__x5f97__x5206_即为当前字段的内部名称
    列内部名称

Word教程

Word插入图片至单元格时固定单元格大小

  • 右键Table选择Table Properties
  • 点击Table标签->点击Options
  • Table Options界面取消勾选Automatically resize to fit contents
  • 点击OK返回
  • 选中即将插入图片的单元格
  • 右键Table Properties->Row标签
  • 勾选Specifiy height(此处可以先任意填写,后续通过拖拽单元格边框修改)
  • 设置Row height is:Exactly
  • 保存后退出
  • 插入图片查看效果

VBA固定Word表格单元格大小

1
2
3
4
5
6
7
Sub DisableResizeTableCells()
Dim meekouTable As Table
Set meekouTable = Selection.Tables(1)
meekouTable.AllowAutoFit = False
meekouTable.Rows.HeightRule = wdRowHeightExactly
meekouTable.Rows.Height = CentimetersToPoints(5)
End Sub

Word插入图片至表格内,不改变单元格原始大小

  • 通过Word Script Lab实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
name: Word不改变表格单元格大小的情况下插入图片 (1)
description: Create a new snippet from a blank template.
host: WORD
api_set: {}
script:
content: |
$("#insertImage").click(() => tryCatch(insertImage));

async function insertImage() {
let fileInput: HTMLInputElement = document.createElement("input");
fileInput.type = "file";
fileInput.style.display = "none";
fileInput.accept = "image/*";
fileInput.onchange = async () => {
var reader: FileReader = new FileReader();
reader.onload = () => {
Word.run(async (context) => {
let base64 = reader.result.toString();
let img = new Image();
let imgWidth = 0;
let imgHeight = 0;
img.src = base64;
img.onload = function() {
imgWidth = img.width;
imgHeight = img.height;
};
base64 = base64.split(",")[1];
console.log(base64);
const body = context.document.getSelection();
let cell = body.parentTableCell;
cell.load("width,parentRow/preferredHeight");
await context.sync();
let width = cell.width;
let height = cell.parentRow.preferredHeight;
let inlinePic = body.insertInlinePictureFromBase64(base64, Word.InsertLocation.replace);
await context.sync();
if (imgWidth > width || imgHeight > height) {
inlinePic.width = width;
inlinePic.height = height;
}
}).catch((error) => {
console.log(error);
});
};
// read in the image file as a data URL.
reader.readAsDataURL(fileInput.files[0]);
};
fileInput.click();
}

/** Default helper for invoking an action and handling errors. */
async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user through your add-in's UI.
console.error(error);
}
}
language: typescript
template:
content: "<div>\n\t<button id=\"insertImage\">插入图片至Word表格内,不改变单元格大小</>\n</div>"
language: html
style:
content: |-
section.samples {
margin-top: 20px;
}

section.samples .ms-Button, section.setup .ms-Button {
display: block;
margin-bottom: 5px;
margin-left: 20px;
min-width: 80px;
}

table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
language: css
libraries: |
https://appsforoffice.microsoft.com/lib/1/hosted/office.js
@types/office-js

office-ui-fabric-js@1.4.0/dist/css/fabric.min.css
office-ui-fabric-js@1.4.0/dist/css/fabric.components.min.css

core-js@2.4.1/client/core.min.js
@types/core-js

jquery@3.1.1
@types/jquery@3.3.1

一键自动插入日报模板

  • 打开Word
  • 插入Script Lab
  • 设置HTML
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <div>
    <button id="insertReport">插入日报</>
    </div>
    <div style="padding:30px">
    <table style="border: 1px solid black;border-collapse: collapse;width:600px">
    <tr style="border: 1px solid black;border-collapse: collapse;">
    <th style="text-align: center;border: 1px solid black;border-collapse: collapse;" colspan="3">工作日报</th>
    </tr>
    <tr style="border: 1px solid black;border-collapse: collapse;">
    <th style="border: 1px solid black;border-collapse: collapse;text-align: left;width:200px">日期: </th>
    <th style="border: 1px solid black;border-collapse: collapse;text-align: left;width:200px">姓名: </th>
    <th style="border: 1px solid black;border-collapse: collapse;text-align: left;width:200px">部门: </th>
    </tr>
    <tr style="border: 1px solid black;border-collapse: collapse;height:250px">
    <th style="border: 1px solid black;border-collapse: collapse;">工作内容</th>
    <th colspan="2" style="border: 1px solid black;border-collapse: collapse;vertical-align:top;padding:0;text-align: left">
    </th>
    </tr >
    <tr style="border: 1px solid black;border-collapse: collapse;height:250px">
    <th style="border: 1px solid black;border-collapse: collapse;">工作总结</th>
    <th colspan="2" style="border: 1px solid black;border-collapse: collapse;text-align:left;vertical-align:top;padding:0">
    </th>
    </tr>
    <tr style="border: 1px solid black;border-collapse: collapse;height:250px;">
    <th style="border: 1px solid black;border-collapse: collapse;">明日计划</th>
    <th colspan="2" style="border: 1px solid black;border-collapse: collapse;text-align:left;vertical-align:top;padding:0">
    </th>
    </tr>
    </table>
    </div>
  • 设置Script
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    $("#insertReport").click(() => tryCatch(insertReport));
    async function insertReport() {
    await Word.run(async (context) => {
    const body = context.document.getSelection();
    const html = document.getElementsByTagName("table")[0].outerHTML;
    body.insertHtml(html, Word.InsertLocation.before);
    await context.sync();
    });
    }

    /** Default helper for invoking an action and handling errors. */
    async function tryCatch(callback) {
    try {
    await callback();
    } catch (error) {
    // Note: In a production add-in, you'd want to notify the user through your add-in's UI.
    console.error(error);
    }
    }

图片与base64字符串的相互转化

将本地图片转化为base64字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Public Function ConvertFileToBase64(filePath As String) As String

Const UseBinaryStreamType = 1

Dim objStream: Set objStream = CreateObject("ADODB.Stream")
Dim objXmlDoc: Set objXmlDoc = CreateObject("Microsoft.XMLDOM")
Dim objXmlElem: Set objXmlElem = objXmlDoc.createElement("tmp")

objStream.Open
objStream.Type = UseBinaryStreamType
objStream.LoadFromFile filePath
objXmlElem.DataType = "bin.base64"
objXmlElem.nodeTypedValue = objStream.Read
ConvertFileToBase64 = Replace(objXmlElem.Text, vbLf, "")

Set objStream = Nothing
Set objXmlDoc = Nothing
Set objXmlElem = Nothing

End Function

将base64字符串保存为本地图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Public Sub ConvertBase64ToFile(filePath As String, strBase64 As String)

Const UseBinaryStreamType = 1
Const SaveWillCreateOrOverwrite = 2

Dim objStreamOutput: Set objStreamOutput = CreateObject("ADODB.Stream")
Dim objXmlDoc: Set objXmlDoc = CreateObject("Microsoft.XMLDOM")
Dim objXmlElem: Set objXmlElem = objXmlDoc.createElement("tmp")

objXmlElem.DataType = "bin.base64"
objXmlElem.Text = strBase64
objStreamOutput.Open
objStreamOutput.Type = UseBinaryStreamType
objStreamOutput.Write = objXmlElem.nodeTypedValue
objStreamOutput.SaveToFile filePath, SaveWillCreateOrOverwrite

Set objStreamOutput = Nothing
Set objXmlDoc = Nothing
Set objXmlElem = Nothing

End Sub

VBA将base64字符串插入Excel图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Sub InsertImgFromBase64(sheet As String, address As String, varImgName As String, varBase64 As String)
Dim tempFile As String
'Save base64 string to temp folder
tempFile = Environ("temp") & "\" & varImgName

'save byte array to temp file
Open tempFile For Binary As #1
Put #1, 1, DecodeBase64(varBase64)
Close #1

'insert image from temp file
Sheets(sheet).Range(address).Select
Sheets(sheet).Pictures.Insert tempFile

'kill temp file
Kill tempFile

End Sub

Private Function DecodeBase64(ByVal strData As String) As Byte()

Dim objXML As Object 'MSXML2.DOMDocument
Dim objNode As Object 'MSXML2.IXMLDOMElement

'get dom document
Set objXML = CreateObject("MSXML2.DOMDocument")

'create node with type of base 64 and decode
Set objNode = objXML.createElement("b64")
objNode.DataType = "bin.base64"
objNode.Text = strData
DecodeBase64 = objNode.nodeTypedValue

'clean up
Set objNode = Nothing
Set objXML = Nothing

End Function

Sub InsertImgFromBase64Test()
InsertImgFromBase64 "Sheet1", "E15", "test2.png", Range("A1")
End Sub

Scripts实现Excel Online插入base64图片

1
2
3
4
5
6
7
function main(workbook: ExcelScript.Workbook, sheetName: string, address: string, base64ImageString: string) {
let sheet = workbook.getWorksheet(sheetName);
let range = sheet.getRange(address);
let image = sheet.addImage(base64ImageString);
image.setTop(range.getTop());
image.setLeft(range.getLeft());
}

个人所得税之退税还是补税?

需求描述

2021年综合所得年度汇算即将于2022年3月1号正式开始,目前已经可以进行申报预约。相信很多打工人已经开始关心自己是会退税还是需要再次进行补税。

需要申报的情况如下:

  • 年度已预缴税额大于年度应纳税额且申请退税的
  • 年度综合所得收入>12万元且需要补税金额>400元的

那么,退税还是补税,就需要计算年度应纳税额。那么怎么计算年度应纳税额呢?

需求分析

计算年度应纳税额需要分两种情况

  • 年终奖单独计税
  • 年终奖综合计税

年终奖单独计税

年收入计税 =

过滤后设置合并单元格的背景色

需求描述

在处理Excel数据的时候,有时候,我们会对Excel的数据进行单元格合并,然后再过滤设置单元格的背景色,一般情况下都不会有问题,但是当过滤后对合并单元格设置背景色时,如果当前行刚好不是合并单元格之前所显示的内容,就会导致在过滤的情况下,无法对当前的单元格设置背景色

需求分析

问题是由于,设置合并单元格的背景色,需要对合并前单元格的背景色进行设置,而如果我们过滤了,就有可能存在过滤了刚好为空的单元格

需求实现

通过VBA实现自定义填充单元格背景色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Sub ColorMergeCells()
Dim current As Range
Dim mergedCell As Range
For Each current In Range("B1:B10").SpecialCells(xlCellTypeVisible)
If current.Offset(0, -1).MergeCells Then
For Each mergedCell In current.Offset(0, -1).MergeArea
If mergedCell.Text <> "" Then
mergedCell.Interior.Color = current.Interior.Color
End If
Next
End If

Next
End Sub

Opencv手册

Image

Convert a numpy array to image

1
2
3
4
5
6
7
from PIL import Image
import numpy as np

w, h = 512, 512
data = np.zeros((h, w, 3), dtype=np.uint8)
data[0:256, 0:256] = [255, 0, 0] # red patch in upper left
img = Image.fromarray(data, 'RGB')